diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 18:44:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 18:44:42 +0300 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /spec/finders | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/finders')
29 files changed, 980 insertions, 172 deletions
diff --git a/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb b/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb new file mode 100644 index 00000000000..0275205028a --- /dev/null +++ b/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Analytics::CycleAnalytics::StageFinder do + let(:project) { build(:project) } + + let(:stage_id) { { id: Gitlab::Analytics::CycleAnalytics::DefaultStages.names.first } } + + subject { described_class.new(parent: project, stage_id: stage_id[:id]).execute } + + context 'when looking up in-memory default stage by name exists' do + it { expect(subject).not_to be_persisted } + it { expect(subject.name).to eq(stage_id[:id]) } + end + + context 'when in-memory default stage cannot be found' do + before do + stage_id[:id] = 'unknown_default_stage' + end + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + end +end diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb index d4795d786bc..4df026f2f5f 100644 --- a/spec/finders/ci/runners_finder_spec.rb +++ b/spec/finders/ci/runners_finder_spec.rb @@ -25,10 +25,12 @@ RSpec.describe Ci::RunnersFinder do end context 'filter by status' do - it 'calls the corresponding scope on Ci::Runner' do - expect(Ci::Runner).to receive(:paused).and_call_original + Ci::Runner::AVAILABLE_STATUSES.each do |status| + it "calls the corresponding :#{status} scope on Ci::Runner" do + expect(Ci::Runner).to receive(status.to_sym).and_call_original - described_class.new(current_user: admin, params: { status_status: 'paused' }).execute + described_class.new(current_user: admin, params: { status_status: status }).execute + end end end @@ -70,17 +72,6 @@ RSpec.describe Ci::RunnersFinder do end end - context 'paginate' do - it 'returns the runners for the specified page' do - stub_const('Ci::RunnersFinder::NUMBER_OF_RUNNERS_PER_PAGE', 1) - runner1 = create :ci_runner, created_at: '2018-07-12 07:00' - runner2 = create :ci_runner, created_at: '2018-07-12 08:00' - - expect(described_class.new(current_user: admin, params: { page: 1 }).execute).to eq [runner2] - expect(described_class.new(current_user: admin, params: { page: 2 }).execute).to eq [runner1] - end - end - context 'non admin user' do it 'returns no runners' do user = create :user @@ -170,38 +161,6 @@ RSpec.describe Ci::RunnersFinder do end end - context 'paginate' do - using RSpec::Parameterized::TableSyntax - - let(:runners) do - [[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 - - where(:page, :index) do - 1 | 0 - 2 | 1 - 3 | 2 - 4 | 3 - end - - before do - stub_const('Ci::RunnersFinder::NUMBER_OF_RUNNERS_PER_PAGE', 3) - - group.add_owner(user) - end - - with_them do - let(:params) { { page: page } } - - it 'returns the runners for the specified page' do - expect(subject).to eq(runners[index]) - end - end - end - context 'filter by search term' do let(:params) { { search: 'runner_project_search' } } diff --git a/spec/finders/concerns/packages/finder_helper_spec.rb b/spec/finders/concerns/packages/finder_helper_spec.rb index c1740ee1796..bad4c482bc6 100644 --- a/spec/finders/concerns/packages/finder_helper_spec.rb +++ b/spec/finders/concerns/packages/finder_helper_spec.rb @@ -3,6 +3,30 @@ require 'spec_helper' RSpec.describe ::Packages::FinderHelper do + describe '#packages_for_project' do + let_it_be_with_reload(:project1) { create(:project) } + let_it_be(:package1) { create(:package, project: project1) } + let_it_be(:package2) { create(:package, :error, project: project1) } + let_it_be(:project2) { create(:project) } + let_it_be(:package3) { create(:package, project: project2) } + + let(:finder_class) do + Class.new do + include ::Packages::FinderHelper + + def execute(project1) + packages_for_project(project1) + end + end + end + + let(:finder) { finder_class.new } + + subject { finder.execute(project1) } + + it { is_expected.to eq [package1]} + end + describe '#packages_visible_to_user' do using RSpec::Parameterized::TableSyntax @@ -12,6 +36,7 @@ RSpec.describe ::Packages::FinderHelper do let_it_be_with_reload(:subgroup) { create(:group, parent: group) } let_it_be_with_reload(:project2) { create(:project, namespace: subgroup) } let_it_be(:package2) { create(:package, project: project2) } + let_it_be(:package3) { create(:package, :error, project: project2) } let(:finder_class) do Class.new do diff --git a/spec/finders/deploy_tokens/tokens_finder_spec.rb b/spec/finders/deploy_tokens/tokens_finder_spec.rb new file mode 100644 index 00000000000..7f19c5bf11b --- /dev/null +++ b/spec/finders/deploy_tokens/tokens_finder_spec.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DeployTokens::TokensFinder do + include AdminModeHelper + + let_it_be(:admin) { create(:admin) } + let_it_be(:user) { create(:user) } + let_it_be(:other_user) { create(:user) } + let_it_be(:project) { create(:project, creator_id: user.id) } + let_it_be(:group) { create(:group) } + + let!(:project_deploy_token) { create(:deploy_token, projects: [project]) } + let!(:revoked_project_deploy_token) { create(:deploy_token, projects: [project], revoked: true) } + let!(:expired_project_deploy_token) { create(:deploy_token, projects: [project], expires_at: '1988-01-11T04:33:04-0600') } + let!(:group_deploy_token) { create(:deploy_token, :group, groups: [group]) } + let!(:revoked_group_deploy_token) { create(:deploy_token, :group, groups: [group], revoked: true) } + let!(:expired_group_deploy_token) { create(:deploy_token, :group, groups: [group], expires_at: '1988-01-11T04:33:04-0600') } + + describe "#execute" do + let(:params) { {} } + + context 'when scope is :all' do + subject { described_class.new(admin, :all, params).execute } + + before do + enable_admin_mode!(admin) + end + + it 'returns all deploy tokens' do + expect(subject.size).to eq(6) + is_expected.to match_array([ + project_deploy_token, + revoked_project_deploy_token, + expired_project_deploy_token, + group_deploy_token, + revoked_group_deploy_token, + expired_group_deploy_token + ]) + end + + context 'and active filter is applied' do + let(:params) { { active: true } } + + it 'returns only active tokens' do + is_expected.to match_array([ + project_deploy_token, + group_deploy_token + ]) + end + end + + context 'but user is not an admin' do + subject { described_class.new(user, :all, params).execute } + + it 'raises Gitlab::Access::AccessDeniedError' do + expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) + end + end + end + + context 'when scope is a Project' do + subject { described_class.new(user, project, params).execute } + + before do + project.add_maintainer(user) + end + + it 'returns all deploy tokens for the project' do + is_expected.to match_array([ + project_deploy_token, + revoked_project_deploy_token, + expired_project_deploy_token + ]) + end + + context 'and active filter is applied' do + let(:params) { { active: true } } + + it 'returns only active tokens for the project' do + is_expected.to match_array([project_deploy_token]) + end + end + + context 'but user is not a member' do + subject { described_class.new(other_user, :all, params).execute } + + it 'raises Gitlab::Access::AccessDeniedError' do + expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) + end + end + end + + context 'when scope is a Group' do + subject { described_class.new(user, group, params).execute } + + before do + group.add_maintainer(user) + end + + it 'returns all deploy tokens for the group' do + is_expected.to match_array([ + group_deploy_token, + revoked_group_deploy_token, + expired_group_deploy_token + ]) + end + + context 'and active filter is applied' do + let(:params) { { active: true } } + + it 'returns only active tokens for the group' do + is_expected.to match_array([group_deploy_token]) + end + end + + context 'but user is not a member' do + subject { described_class.new(other_user, :all, params).execute } + + it 'raises Gitlab::Access::AccessDeniedError' do + expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) + end + end + end + + context 'when scope is nil' do + subject { described_class.new(user, nil, params).execute } + + it 'raises ArgumentError' do + expect { subject }.to raise_error(ArgumentError) + end + end + end +end diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb index 0f659fa1dab..b294f1117f5 100644 --- a/spec/finders/deployments_finder_spec.rb +++ b/spec/finders/deployments_finder_spec.rb @@ -5,6 +5,58 @@ require 'spec_helper' RSpec.describe DeploymentsFinder do subject { described_class.new(params).execute } + describe "validation" do + context 'when both updated_at and finished_at filters are specified' do + let(:params) { { updated_before: 1.day.ago, finished_before: 1.day.ago } } + + it 'raises an error' do + expect { subject }.to raise_error( + described_class::InefficientQueryError, + 'Both `updated_at` filter and `finished_at` filter can not be specified') + end + end + + context 'when updated_at filter and id sorting' do + let(:params) { { updated_before: 1.day.ago, order_by: :id } } + + it 'raises an error' do + expect { subject }.to raise_error( + described_class::InefficientQueryError, + '`updated_at` filter and `updated_at` sorting must be paired') + end + end + + context 'when finished_at filter and id sorting' do + let(:params) { { finished_before: 1.day.ago, order_by: :id } } + + it 'raises an error' do + expect { subject }.to raise_error( + described_class::InefficientQueryError, + '`finished_at` filter and `finished_at` sorting must be paired') + end + end + + context 'when finished_at filter with failed status filter' do + let(:params) { { finished_before: 1.day.ago, order_by: :finished_at, status: :failed } } + + it 'raises an error' do + expect { subject }.to raise_error( + described_class::InefficientQueryError, + '`finished_at` filter must be combined with `success` status filter.') + end + end + + context 'when environment filter with non-project scope' do + let(:params) { { environment: 'production' } } + + it 'raises an error' do + expect { subject }.to raise_error( + described_class::InefficientQueryError, + '`environment` filter must be combined with `project` scope.') + end + end + end + describe "#execute" do context 'when project or group is missing' do let(:params) { {} } @@ -20,13 +72,24 @@ RSpec.describe DeploymentsFinder do describe 'filtering' do context 'when updated_at filters are specified' do - let(:params) { { **base_params, updated_before: 1.day.ago, updated_after: 3.days.ago } } - let!(:deployment_1) { create(:deployment, :success, project: project, updated_at: 2.days.ago) } - let!(:deployment_2) { create(:deployment, :success, project: project, updated_at: 4.days.ago) } - let!(:deployment_3) { create(:deployment, :success, project: project, updated_at: 1.hour.ago) } + let_it_be(:deployment_1) { create(:deployment, :success, project: project, updated_at: 48.hours.ago) } + let_it_be(:deployment_2) { create(:deployment, :success, project: project, updated_at: 47.hours.ago) } + let_it_be(:deployment_3) { create(:deployment, :success, project: project, updated_at: 4.days.ago) } + let_it_be(:deployment_4) { create(:deployment, :success, project: project, updated_at: 1.hour.ago) } + let(:params) { { **base_params, updated_before: 1.day.ago, updated_after: 3.days.ago, order_by: :updated_at } } it 'returns deployments with matched updated_at' do - is_expected.to match_array([deployment_1]) + is_expected.to match_array([deployment_2, deployment_1]) + end + + context 'when deployments_finder_implicitly_enforce_ordering_for_updated_at_filter feature flag is disabled' do + before do + stub_feature_flags(deployments_finder_implicitly_enforce_ordering_for_updated_at_filter: false) + end + + it 'returns deployments with matched updated_at' do + is_expected.to match_array([deployment_1, deployment_2]) + end end end @@ -72,30 +135,34 @@ RSpec.describe DeploymentsFinder do let(:params) { { **base_params, order_by: order_by, sort: sort } } - let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) } - let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) } - let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) } + let!(:deployment_1) { create(:deployment, :success, project: project, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) } + let!(:deployment_2) { create(:deployment, :success, project: project, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) } + let!(:deployment_3) { create(:deployment, :success, project: project, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) } where(:order_by, :sort, :ordered_deployments) do 'created_at' | 'asc' | [:deployment_1, :deployment_2, :deployment_3] 'created_at' | 'desc' | [:deployment_3, :deployment_2, :deployment_1] 'id' | 'asc' | [:deployment_1, :deployment_2, :deployment_3] 'id' | 'desc' | [:deployment_3, :deployment_2, :deployment_1] - 'iid' | 'asc' | [:deployment_3, :deployment_1, :deployment_2] - 'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3] + 'iid' | 'asc' | [:deployment_1, :deployment_2, :deployment_3] + 'iid' | 'desc' | [:deployment_3, :deployment_2, :deployment_1] 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3] 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2] - 'updated_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1] - 'updated_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2] - 'finished_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1] - 'finished_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2] + 'updated_at' | 'asc' | described_class::InefficientQueryError + 'updated_at' | 'desc' | described_class::InefficientQueryError + 'finished_at' | 'asc' | described_class::InefficientQueryError + 'finished_at' | 'desc' | described_class::InefficientQueryError 'invalid' | 'asc' | [:deployment_1, :deployment_2, :deployment_3] - 'iid' | 'err' | [:deployment_3, :deployment_1, :deployment_2] + 'iid' | 'err' | [:deployment_1, :deployment_2, :deployment_3] end with_them do it 'returns the deployments ordered' do - expect(subject).to eq(ordered_deployments.map { |name| public_send(name) }) + if ordered_deployments == described_class::InefficientQueryError + expect { subject }.to raise_error(described_class::InefficientQueryError) + else + expect(subject).to eq(ordered_deployments.map { |name| public_send(name) }) + end end end end @@ -112,8 +179,20 @@ RSpec.describe DeploymentsFinder do end end - describe 'tie-breaker for `finished_at` sorting' do - let(:params) { { **base_params, order_by: 'updated_at', sort: 'asc' } } + describe 'transform `iid` sorting to `id` sorting' do + let(:params) { { **base_params, order_by: 'iid', sort: 'asc' } } + + it 'sorts by only one column' do + expect(subject.order_values.size).to eq(1) + end + + it 'sorts by `id`' do + expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql) + end + end + + describe 'tie-breaker for `updated_at` sorting' do + let(:params) { { **base_params, updated_after: 1.day.ago, order_by: 'updated_at', sort: 'asc' } } it 'sorts by two columns' do expect(subject.order_values.size).to eq(2) @@ -122,17 +201,62 @@ RSpec.describe DeploymentsFinder do it 'adds `id` sorting as the second order column' do order_value = subject.order_values[1] - expect(order_value.to_sql).to eq(Deployment.arel_table[:id].desc.to_sql) + expect(order_value.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql) end - it 'uses the `id DESC` as tie-breaker when ordering' do + it 'uses the `id ASC` as tie-breaker when ordering' do updated_at = Time.now deployment_1 = create(:deployment, :success, project: project, updated_at: updated_at) deployment_2 = create(:deployment, :success, project: project, updated_at: updated_at) deployment_3 = create(:deployment, :success, project: project, updated_at: updated_at) - expect(subject).to eq([deployment_3, deployment_2, deployment_1]) + expect(subject).to eq([deployment_1, deployment_2, deployment_3]) + end + + context 'when sort direction is desc' do + let(:params) { { **base_params, updated_after: 1.day.ago, order_by: 'updated_at', sort: 'desc' } } + + it 'uses the `id DESC` as tie-breaker when ordering' do + updated_at = Time.now + + deployment_1 = create(:deployment, :success, project: project, updated_at: updated_at) + deployment_2 = create(:deployment, :success, project: project, updated_at: updated_at) + deployment_3 = create(:deployment, :success, project: project, updated_at: updated_at) + + expect(subject).to eq([deployment_3, deployment_2, deployment_1]) + end + end + end + + describe 'enforce sorting to `updated_at` sorting' do + let(:params) { { **base_params, updated_before: 1.day.ago, order_by: 'id', sort: 'asc' } } + + before do + allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception) + end + + it 'sorts by only one column' do + expect(subject.order_values.size).to eq(2) + end + + it 'sorts by `updated_at`' do + expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:updated_at].asc.to_sql) + expect(subject.order_values.second.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql) + end + + context 'when deployments_finder_implicitly_enforce_ordering_for_updated_at_filter feature flag is disabled' do + before do + stub_feature_flags(deployments_finder_implicitly_enforce_ordering_for_updated_at_filter: false) + end + + it 'sorts by only one column' do + expect(subject.order_values.size).to eq(1) + end + + it 'sorts by `id`' do + expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql) + end end end @@ -142,23 +266,76 @@ RSpec.describe DeploymentsFinder do let!(:deployment_3) { create(:deployment, :success, project: project, finished_at: 5.hours.ago) } context 'when filtering by finished_after and finished_before' do - let(:params) { { **base_params, finished_after: 3.days.ago, finished_before: 1.day.ago } } + let(:params) { { **base_params, finished_after: 3.days.ago, finished_before: 1.day.ago, status: :success, order_by: :finished_at } } it { is_expected.to match_array([deployment_1]) } end context 'when the finished_before parameter is missing' do - let(:params) { { **base_params, finished_after: 3.days.ago } } + let(:params) { { **base_params, finished_after: 3.days.ago, status: :success, order_by: :finished_at } } it { is_expected.to match_array([deployment_1, deployment_3]) } end context 'when finished_after is missing' do - let(:params) { { **base_params, finished_before: 3.days.ago } } + let(:params) { { **base_params, finished_before: 3.days.ago, status: :success, order_by: :finished_at } } it { is_expected.to match_array([deployment_2]) } end end end + + context 'at group scope' do + let_it_be(:group) { create(:group) } + let_it_be(:subgroup) { create(:group, parent: group) } + + let_it_be(:group_project_1) { create(:project, :public, :test_repo, group: group) } + let_it_be(:group_project_2) { create(:project, :public, :test_repo, group: group) } + let_it_be(:subgroup_project_1) { create(:project, :public, :test_repo, group: subgroup) } + let(:base_params) { { group: group } } + + describe 'ordering' do + using RSpec::Parameterized::TableSyntax + + let(:params) { { **base_params, order_by: order_by, sort: sort } } + + let!(:group_project_1_deployment) { create(:deployment, :success, project: group_project_1, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) } + let!(:group_project_2_deployment) { create(:deployment, :success, project: group_project_2, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) } + let!(:subgroup_project_1_deployment) { create(:deployment, :success, project: subgroup_project_1, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) } + + where(:order_by, :sort) do + 'created_at' | 'asc' + 'created_at' | 'desc' + 'id' | 'asc' + 'id' | 'desc' + 'iid' | 'asc' + 'iid' | 'desc' + 'ref' | 'asc' + 'ref' | 'desc' + 'invalid' | 'asc' + 'iid' | 'err' + end + + with_them do + it 'returns the deployments unordered' do + expect(subject.to_a).to contain_exactly(group_project_1_deployment, + group_project_2_deployment, + subgroup_project_1_deployment) + end + end + end + + it 'avoids N+1 queries' do + execute_queries = -> { described_class.new({ group: group }).execute.first } + control_count = ActiveRecord::QueryRecorder.new { execute_queries }.count + + new_project = create(:project, :repository, group: group) + new_env = create(:environment, project: new_project, name: "production") + create_list(:deployment, 2, status: :success, project: new_project, environment: new_env) + group.reload + + expect { execute_queries }.not_to exceed_query_limit(control_count) + end + end end end diff --git a/spec/finders/environment_names_finder_spec.rb b/spec/finders/environments/environment_names_finder_spec.rb index fe00c800f0a..438f9e9ea7c 100644 --- a/spec/finders/environment_names_finder_spec.rb +++ b/spec/finders/environments/environment_names_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentNamesFinder do +RSpec.describe Environments::EnvironmentNamesFinder do describe '#execute' do let!(:group) { create(:group) } let!(:public_project) { create(:project, :public, namespace: group) } diff --git a/spec/finders/environments_by_deployments_finder_spec.rb b/spec/finders/environments/environments_by_deployments_finder_spec.rb index f5fcc4ef72a..1b86aced67d 100644 --- a/spec/finders/environments_by_deployments_finder_spec.rb +++ b/spec/finders/environments/environments_by_deployments_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentsByDeploymentsFinder do +RSpec.describe Environments::EnvironmentsByDeploymentsFinder do let(:project) { create(:project, :repository) } let(:user) { project.creator } let(:environment) { create(:environment, :available, project: project) } diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments/environments_finder_spec.rb index c2022331ad9..68c0c524478 100644 --- a/spec/finders/environments_finder_spec.rb +++ b/spec/finders/environments/environments_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe EnvironmentsFinder do +RSpec.describe Environments::EnvironmentsFinder do let(:project) { create(:project, :repository) } let(:user) { project.creator } let(:environment) { create(:environment, :available, project: project) } diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index a2aac857bf5..27466ab563f 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -1178,6 +1178,7 @@ RSpec.describe IssuesFinder do it 'returns true' do expect(finder.use_cte_for_search?).to be_truthy + expect(finder.execute.to_sql).to match(/^WITH "issues" AS #{Gitlab::Database::AsWithMaterialized.materialized_if_supported}/) end end @@ -1186,6 +1187,7 @@ RSpec.describe IssuesFinder do it 'returns true' do expect(finder.use_cte_for_search?).to be_truthy + expect(finder.execute.to_sql).to match(/^WITH "issues" AS #{Gitlab::Database::AsWithMaterialized.materialized_if_supported}/) end end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 597d22801ca..3b835d366db 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -866,5 +866,36 @@ RSpec.describe MergeRequestsFinder do end end end + + describe '#count_by_state' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:labels) { create_list(:label, 2, project: project) } + let_it_be(:merge_requests) { create_list(:merge_request, 4, :unique_branches, author: user, target_project: project, source_project: project, labels: labels) } + + before do + project.add_developer(user) + end + + context 'when filtering by multiple labels' do + it 'returns the correnct counts' do + counts = described_class.new(user, { label_name: labels.map(&:name) }).count_by_state + + expect(counts[:all]).to eq(merge_requests.size) + end + end + + context 'when filtering by approved_by_usernames' do + before do + merge_requests.each { |mr| mr.approved_by_users << user } + end + + it 'returns the correnct counts' do + counts = described_class.new(user, { approved_by_usernames: [user.username] }).count_by_state + + expect(counts[:all]).to eq(merge_requests.size) + end + end + end end end diff --git a/spec/finders/packages/composer/packages_finder_spec.rb b/spec/finders/packages/composer/packages_finder_spec.rb new file mode 100644 index 00000000000..d4328827de3 --- /dev/null +++ b/spec/finders/packages/composer/packages_finder_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe ::Packages::Composer::PackagesFinder do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + + let(:params) { {} } + + describe '#execute' do + let_it_be(:composer_package) { create(:composer_package, project: project) } + let_it_be(:composer_package2) { create(:composer_package, project: project) } + let_it_be(:error_package) { create(:composer_package, :error, project: project) } + let_it_be(:composer_package3) { create(:composer_package) } + + subject { described_class.new(user, group, params).execute } + + before do + project.add_developer(user) + end + + it { is_expected.to match_array([composer_package, composer_package2]) } + end +end diff --git a/spec/finders/packages/conan/package_finder_spec.rb b/spec/finders/packages/conan/package_finder_spec.rb index 936a0e5ff4b..b26f8900090 100644 --- a/spec/finders/packages/conan/package_finder_spec.rb +++ b/spec/finders/packages/conan/package_finder_spec.rb @@ -11,7 +11,8 @@ RSpec.describe ::Packages::Conan::PackageFinder do subject { described_class.new(user, query: query).execute } - context 'packages that are not visible to user' do + context 'packages that are not installable' do + let!(:conan_package3) { create(:conan_package, :error, project: project) } let!(:non_visible_project) { create(:project, :private) } let!(:non_visible_conan_package) { create(:conan_package, project: non_visible_project) } let(:query) { "#{conan_package.name.split('/').first[0, 3]}%" } diff --git a/spec/finders/packages/generic/package_finder_spec.rb b/spec/finders/packages/generic/package_finder_spec.rb index ed34268e7a9..707f943b285 100644 --- a/spec/finders/packages/generic/package_finder_spec.rb +++ b/spec/finders/packages/generic/package_finder_spec.rb @@ -23,6 +23,13 @@ RSpec.describe ::Packages::Generic::PackageFinder do expect(found_package).to eq(package) end + it 'does not find uninstallable packages' do + error_package = create(:generic_package, :error, project: project) + + expect { finder.execute!(error_package.name, error_package.version) } + .to raise_error(ActiveRecord::RecordNotFound) + end + it 'raises ActiveRecord::RecordNotFound if package is not found' do expect { finder.execute!(package.name, '3.1.4') } .to raise_error(ActiveRecord::RecordNotFound) diff --git a/spec/finders/packages/go/package_finder_spec.rb b/spec/finders/packages/go/package_finder_spec.rb index b6fad1e7061..dbcb8255d47 100644 --- a/spec/finders/packages/go/package_finder_spec.rb +++ b/spec/finders/packages/go/package_finder_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Packages::Go::PackageFinder do let_it_be(:mod) { create :go_module, project: project } let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.1' } - let_it_be(:package) { create :golang_package, project: project, name: mod.name, version: 'v1.0.1' } + let_it_be_with_refind(:package) { create :golang_package, project: project, name: mod.name, version: 'v1.0.1' } let(:finder) { described_class.new(project, mod_name, version_name) } @@ -54,6 +54,17 @@ RSpec.describe Packages::Go::PackageFinder do it { is_expected.to eq(package) } end + context 'with an uninstallable package' do + let(:mod_name) { mod.name } + let(:version_name) { version.name } + + before do + package.update_column(:status, 1) + end + + it { is_expected.to eq(nil) } + end + context 'with an invalid name' do let(:mod_name) { 'foo/bar' } let(:version_name) { 'baz' } diff --git a/spec/finders/packages/group_or_project_package_finder_spec.rb b/spec/finders/packages/group_or_project_package_finder_spec.rb new file mode 100644 index 00000000000..aaeec8e70d2 --- /dev/null +++ b/spec/finders/packages/group_or_project_package_finder_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::GroupOrProjectPackageFinder do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + + let(:finder) { described_class.new(user, project) } + + describe 'execute' do + subject(:run_finder) { finder.execute } + + it { expect { run_finder }.to raise_error(NotImplementedError) } + end + + describe 'execute!' do + subject(:run_finder) { finder.execute! } + + it { expect { run_finder }.to raise_error(NotImplementedError) } + end +end diff --git a/spec/finders/packages/group_packages_finder_spec.rb b/spec/finders/packages/group_packages_finder_spec.rb index d6daf73aba2..29b2f0fffd7 100644 --- a/spec/finders/packages/group_packages_finder_spec.rb +++ b/spec/finders/packages/group_packages_finder_spec.rb @@ -122,7 +122,7 @@ RSpec.describe Packages::GroupPackagesFinder do end context 'when there are processing packages' do - let_it_be(:package4) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) } + let_it_be(:package4) { create(:nuget_package, :processing, project: project) } it { is_expected.to match_array([package1, package2]) } end diff --git a/spec/finders/packages/maven/package_finder_spec.rb b/spec/finders/packages/maven/package_finder_spec.rb index ca144292501..13c603f1ec4 100644 --- a/spec/finders/packages/maven/package_finder_spec.rb +++ b/spec/finders/packages/maven/package_finder_spec.rb @@ -6,13 +6,12 @@ RSpec.describe ::Packages::Maven::PackageFinder do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, namespace: group) } - let_it_be(:package) { create(:maven_package, project: project) } + let_it_be_with_refind(:package) { create(:maven_package, project: project) } let(:param_path) { nil } - let(:param_project) { nil } - let(:param_group) { nil } + let(:project_or_group) { nil } let(:param_order_by_package_file) { false } - let(:finder) { described_class.new(param_path, user, project: param_project, group: param_group, order_by_package_file: param_order_by_package_file) } + 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) @@ -36,34 +35,28 @@ RSpec.describe ::Packages::Maven::PackageFinder do expect { subject }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'with an uninstallable package' do + let(:param_path) { package.maven_metadatum.path } + + before do + package.update_column(:status, 1) + end + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + end end context 'within the project' do - let(:param_project) { project } + let(:project_or_group) { project } it_behaves_like 'handling valid and invalid paths' end context 'within a group' do - let(:param_group) { group } - - context 'with maven_packages_group_level_improvements enabled' do - before do - stub_feature_flags(maven_packages_group_level_improvements: true) - expect(finder).to receive(:packages_visible_to_user).with(user, within_group: group).and_call_original - end - - it_behaves_like 'handling valid and invalid paths' - end - - context 'with maven_packages_group_level_improvements disabled' do - before do - stub_feature_flags(maven_packages_group_level_improvements: false) - expect(finder).not_to receive(:packages_visible_to_user) - end + let(:project_or_group) { group } - it_behaves_like 'handling valid and invalid paths' - end + it_behaves_like 'handling valid and invalid paths' end context 'across all projects' do @@ -83,7 +76,7 @@ RSpec.describe ::Packages::Maven::PackageFinder do let_it_be(:package2) { create(:maven_package, project: project2, name: package_name, version: nil) } let_it_be(:package3) { create(:maven_package, project: project3, name: package_name, version: nil) } - let(:param_group) { group } + let(:project_or_group) { group } let(:param_path) { package_name } before do @@ -93,38 +86,14 @@ RSpec.describe ::Packages::Maven::PackageFinder do create(:package_file, :xml, package: package2) end - context 'with maven_packages_group_level_improvements enabled' do - before do - stub_feature_flags(maven_packages_group_level_improvements: true) - expect(finder).not_to receive(:versionless_package?) - end - - context 'without order by package file' do - it { is_expected.to eq(package3) } - end - - context 'with order by package file' do - let(:param_order_by_package_file) { true } - - it { is_expected.to eq(package2) } - end + context 'without order by package file' do + it { is_expected.to eq(package3) } end - context 'with maven_packages_group_level_improvements disabled' do - before do - stub_feature_flags(maven_packages_group_level_improvements: false) - expect(finder).to receive(:versionless_package?).and_call_original - end + context 'with order by package file' do + let(:param_order_by_package_file) { true } - context 'without order by package file' do - it { is_expected.to eq(package2) } - end - - context 'with order by package file' do - let(:param_order_by_package_file) { true } - - it { is_expected.to eq(package2) } - end + it { is_expected.to eq(package2) } end end end @@ -146,7 +115,7 @@ RSpec.describe ::Packages::Maven::PackageFinder do it_behaves_like 'Packages::Maven::PackageFinder examples' it 'uses CTE in the query' do - sql = described_class.new('some_path', user, group: group).send(:packages_with_path).to_sql + sql = described_class.new(user, group, path: package.maven_metadatum.path).send(:packages).to_sql expect(sql).to include('WITH "maven_metadata_by_path" AS') end diff --git a/spec/finders/packages/npm/package_finder_spec.rb b/spec/finders/packages/npm/package_finder_spec.rb index f021d800f31..a995f3b96c4 100644 --- a/spec/finders/packages/npm/package_finder_spec.rb +++ b/spec/finders/packages/npm/package_finder_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe ::Packages::Npm::PackageFinder do let_it_be_with_reload(:project) { create(:project)} - let_it_be(:package) { create(:npm_package, project: project) } + let_it_be_with_refind(:package) { create(:npm_package, project: project) } let(:project) { package.project } let(:package_name) { package.name } @@ -46,6 +46,14 @@ RSpec.describe ::Packages::Npm::PackageFinder do it { is_expected.to be_empty } end + + context 'with an uninstallable package' do + before do + package.update_column(:status, 1) + end + + it { is_expected.to be_empty } + end end subject { finder.execute } diff --git a/spec/finders/packages/nuget/package_finder_spec.rb b/spec/finders/packages/nuget/package_finder_spec.rb index 10b5f6c8ec2..59cca2d06dc 100644 --- a/spec/finders/packages/nuget/package_finder_spec.rb +++ b/spec/finders/packages/nuget/package_finder_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Packages::Nuget::PackageFinder do let_it_be(:group) { create(:group) } let_it_be(:subgroup) { create(:group, parent: group) } let_it_be(:project) { create(:project, namespace: subgroup) } - let_it_be(:package1) { create(:nuget_package, project: project) } + let_it_be_with_refind(:package1) { create(:nuget_package, project: project) } let_it_be(:package2) { create(:nuget_package, name: package1.name, version: '2.0.0', project: project) } let_it_be(:package3) { create(:nuget_package, name: 'Another.Dummy.Package', project: project) } let_it_be(:other_package_1) { create(:nuget_package, name: package1.name, version: package1.version) } @@ -33,6 +33,14 @@ RSpec.describe Packages::Nuget::PackageFinder do it { is_expected.to be_empty } end + context 'with an uninstallable package' do + before do + package1.update_column(:status, 1) + end + + it { is_expected.to contain_exactly(package2) } + end + context 'with valid version' do let(:package_version) { '2.0.0' } diff --git a/spec/finders/packages/package_finder_spec.rb b/spec/finders/packages/package_finder_spec.rb index e8c7404a612..2bb4f05a41d 100644 --- a/spec/finders/packages/package_finder_spec.rb +++ b/spec/finders/packages/package_finder_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe ::Packages::PackageFinder do let_it_be(:project) { create(:project) } - let_it_be(:maven_package) { create(:maven_package, project: project) } + let_it_be_with_refind(:maven_package) { create(:maven_package, project: project) } describe '#execute' do let(:package_id) { maven_package.id } @@ -13,8 +13,18 @@ RSpec.describe ::Packages::PackageFinder do it { is_expected.to eq(maven_package) } + context 'with non-displayable package' do + before do + maven_package.update_column(:status, 1) + end + + it 'raises an exception' do + expect { subject }.to raise_exception(ActiveRecord::RecordNotFound) + end + end + context 'processing packages' do - let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) } + let_it_be(:nuget_package) { create(:nuget_package, :processing, project: project) } let(:package_id) { nuget_package.id } it 'are not returned' do diff --git a/spec/finders/packages/packages_finder_spec.rb b/spec/finders/packages/packages_finder_spec.rb index 0add77a8478..b72f4aab3ec 100644 --- a/spec/finders/packages/packages_finder_spec.rb +++ b/spec/finders/packages/packages_finder_spec.rb @@ -76,7 +76,7 @@ RSpec.describe ::Packages::PackagesFinder do end context 'with processing packages' do - let_it_be(:nuget_package) { create(:nuget_package, project: project, name: Packages::Nuget::TEMPORARY_PACKAGE_NAME) } + let_it_be(:nuget_package) { create(:nuget_package, :processing, project: project) } it { is_expected.to match_array([conan_package, maven_package]) } end diff --git a/spec/finders/packages/pypi/package_finder_spec.rb b/spec/finders/packages/pypi/package_finder_spec.rb new file mode 100644 index 00000000000..7d9eb8a5cd1 --- /dev/null +++ b/spec/finders/packages/pypi/package_finder_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Pypi::PackageFinder do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:project2) { create(:project, group: group) } + let_it_be(:package1) { create(:pypi_package, project: project) } + let_it_be(:package2) { create(:pypi_package, project: project) } + let_it_be(:package3) { create(:pypi_package, project: project2) } + + let(:package_file) { package2.package_files.first } + let(:params) do + { + filename: package_file.file_name, + sha256: package_file.file_sha256 + } + end + + describe 'execute' do + subject { described_class.new(user, scope, params).execute } + + context 'within a project' do + let(:scope) { project } + + it { is_expected.to eq(package2) } + end + + context 'within a group' do + let(:scope) { group } + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + + context 'user with access' do + before do + project.add_developer(user) + end + + it { is_expected.to eq(package2) } + end + end + end +end diff --git a/spec/finders/packages/pypi/packages_finder_spec.rb b/spec/finders/packages/pypi/packages_finder_spec.rb new file mode 100644 index 00000000000..a69c2317261 --- /dev/null +++ b/spec/finders/packages/pypi/packages_finder_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Pypi::PackagesFinder do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:project2) { create(:project, group: group) } + let_it_be(:package1) { create(:pypi_package, project: project) } + let_it_be(:package2) { create(:pypi_package, project: project) } + let_it_be(:package3) { create(:pypi_package, name: package2.name, project: project) } + let_it_be(:package4) { create(:pypi_package, name: package2.name, project: project2) } + + let(:package_name) { package2.name } + + describe 'execute!' do + subject { described_class.new(user, scope, package_name: package_name).execute! } + + shared_examples 'when no package is found' do + context 'non-existing package' do + let(:package_name) { 'none' } + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + end + end + + shared_examples 'when package_name param is a non-normalized name' do + context 'non-existing package' do + let(:package_name) { package2.name.upcase.tr('-', '.') } + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + end + end + + context 'within a project' do + let(:scope) { project } + + it { is_expected.to contain_exactly(package2, package3) } + + it_behaves_like 'when no package is found' + it_behaves_like 'when package_name param is a non-normalized name' + end + + context 'within a group' do + let(:scope) { group } + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + + context 'user with access to only one project' do + before do + project2.add_developer(user) + end + + it { is_expected.to contain_exactly(package4) } + + it_behaves_like 'when no package is found' + it_behaves_like 'when package_name param is a non-normalized name' + + context ' user with access to multiple projects' do + before do + project.add_developer(user) + end + + it { is_expected.to contain_exactly(package2, package3, package4) } + end + end + end + end +end diff --git a/spec/finders/projects/groups_finder_spec.rb b/spec/finders/projects/groups_finder_spec.rb index 89d4edaec7c..7f01b73c7ca 100644 --- a/spec/finders/projects/groups_finder_spec.rb +++ b/spec/finders/projects/groups_finder_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Projects::GroupsFinder do let_it_be(:root_group) { create(:group, :public) } let_it_be(:project_group) { create(:group, :public, parent: root_group) } let_it_be(:shared_group_with_dev_access) { create(:group, :private, parent: root_group) } - let_it_be(:shared_group_with_reporter_access) { create(:group, :private) } + let_it_be(:shared_group_with_reporter_access) { create(:group, :public) } let_it_be(:public_project) { create(:project, :public, group: project_group) } let_it_be(:private_project) { create(:project, :private, group: project_group) } @@ -53,6 +53,24 @@ RSpec.describe Projects::GroupsFinder do is_expected.to match_array([project_group, root_group, shared_group_with_dev_access]) end end + + context 'when shared_visible_only is on' do + let(:params) { super().merge(shared_visible_only: true) } + + it 'returns ancestor and public shared groups' do + is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access]) + end + + context 'when user has access to the private shared group' do + before do + shared_group_with_dev_access.add_guest(current_user) + end + + it 'returns ancestor and shared groups user has access to' do + is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access, shared_group_with_dev_access]) + end + end + end end context 'when skip group option is on' do @@ -74,6 +92,19 @@ RSpec.describe Projects::GroupsFinder do it 'returns ancestor groups for this project' do is_expected.to match_array([project_group, root_group]) end + + context 'when visible shared groups are requested' do + let(:params) do + { + with_shared: true, + shared_visible_only: true + } + end + + it 'returns ancestor groups and public shared groups for this project' do + is_expected.to match_array([project_group, root_group, shared_group_with_reporter_access]) + end + end end end end diff --git a/spec/finders/projects/members/effective_access_level_finder_spec.rb b/spec/finders/projects/members/effective_access_level_finder_spec.rb new file mode 100644 index 00000000000..1112dbd0d6e --- /dev/null +++ b/spec/finders/projects/members/effective_access_level_finder_spec.rb @@ -0,0 +1,257 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + + # The result set is being converted to json just for the ease of testing. + subject { described_class.new(project).execute.as_json } + + context 'for a personal project' do + let_it_be(:project) { create(:project) } + + shared_examples_for 'includes access level of the owner of the project as Maintainer' do + it 'includes access level of the owner of the project as Maintainer' do + expect(subject).to( + contain_exactly( + hash_including( + 'user_id' => project.namespace.owner.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + end + + context 'when the project owner is a member of the project' do + it_behaves_like 'includes access level of the owner of the project as Maintainer' + end + + context 'when the project owner is not explicitly a member of the project' do + before do + project.members.find_by(user_id: project.namespace.owner.id).destroy! + end + + it_behaves_like 'includes access level of the owner of the project as Maintainer' + end + end + + context 'direct members of the project' do + it 'includes access levels of the direct members of the project' do + developer = create(:project_member, :developer, source: project) + maintainer = create(:project_member, :maintainer, source: project) + + expect(subject).to( + include( + hash_including( + 'user_id' => developer.user.id, + 'access_level' => Gitlab::Access::DEVELOPER + ), + hash_including( + 'user_id' => maintainer.user.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + + it 'does not include access levels of users who have requested access to the project' do + member_with_access_request = create(:project_member, :access_request, :developer, source: project) + + expect(subject).not_to( + include( + hash_including( + 'user_id' => member_with_access_request.user.id + ) + ) + ) + end + + it 'includes access levels of users who are in non-active state' do + blocked_member = create(:project_member, :blocked, :developer, source: project) + + expect(subject).to( + include( + hash_including( + 'user_id' => blocked_member.user.id, + 'access_level' => Gitlab::Access::DEVELOPER + ) + ) + ) + end + end + + context 'for a project within a group' do + context 'project in a root group' do + it 'includes access levels of users who are direct members of the parent group' do + group_member = create(:group_member, :developer, source: group) + + expect(subject).to( + include( + hash_including( + 'user_id' => group_member.user.id, + 'access_level' => Gitlab::Access::DEVELOPER + ) + ) + ) + end + end + + context 'project in a subgroup' do + let_it_be(:project) { create(:project, group: create(:group, :nested)) } + + it 'includes access levels of users who are members of the ancestors of the parent group' do + group_member = create(:group_member, :maintainer, source: project.group.parent) + + expect(subject).to( + include( + hash_including( + 'user_id' => group_member.user.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + end + + context 'user is both a member of the project and a member of the parent group' do + let_it_be(:user) { create(:user) } + + before do + group.add_developer(user) + project.add_maintainer(user) + end + + it 'includes the maximum access level among project and group membership' do + expect(subject).to( + include( + hash_including( + 'user_id' => user.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + end + + context 'members from group share' do + let_it_be(:shared_with_group) { create(:group) } + let_it_be(:user_from_shared_with_group) { create(:user) } + + before do + shared_with_group.add_guest(user_from_shared_with_group) + create(:group_group_link, :developer, shared_group: project.group, shared_with_group: shared_with_group) + end + + it 'includes the user from the group share with the right access level' do + expect(subject).to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id, + 'access_level' => Gitlab::Access::GUEST + ) + ) + ) + end + + context 'when the project also has the same user as a member, but with a different access level' do + before do + project.add_maintainer(user_from_shared_with_group) + end + + it 'includes the maximum access level among project and group membership' do + expect(subject).to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + end + + context "when the project's ancestor also has the same user as a member, but with a different access level" do + before do + project.group.add_maintainer(user_from_shared_with_group) + end + + it 'includes the maximum access level among project and group membership' do + expect(subject).to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id, + 'access_level' => Gitlab::Access::MAINTAINER + ) + ) + ) + end + end + end + end + + context 'for a project that is shared with other group(s)' do + let_it_be(:shared_with_group) { create(:group) } + let_it_be(:user_from_shared_with_group) { create(:user) } + + before do + create(:project_group_link, :developer, project: project, group: shared_with_group) + shared_with_group.add_maintainer(user_from_shared_with_group) + end + + it 'includes the least among the specified access levels' do + expect(subject).to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id, + 'access_level' => Gitlab::Access::DEVELOPER + ) + ) + ) + end + + context 'when the group containing the project has forbidden group shares for any of its projects' do + let_it_be(:project) { create(:project, group: create(:group)) } + + before do + project.namespace.update!(share_with_group_lock: true) + end + + it 'does not include the users from any group shares' do + expect(subject).not_to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id + ) + ) + ) + end + end + end + + context 'a combination of all possible avenues of membership' do + let_it_be(:user) { create(:user) } + let_it_be(:shared_with_group) { create(:group) } + + before do + create(:project_group_link, :maintainer, project: project, group: shared_with_group) + create(:group_group_link, :reporter, shared_group: project.group, shared_with_group: shared_with_group) + + shared_with_group.add_maintainer(user) + group.add_guest(user) + project.add_developer(user) + end + + it 'includes the highest access level from all avenues of memberships' do + expect(subject).to( + include( + hash_including( + 'user_id' => user.id, + 'access_level' => Gitlab::Access::MAINTAINER # From project_group_link + ) + ) + ) + end + end +end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index a178261e899..364e5de4ece 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -139,7 +139,7 @@ RSpec.describe ProjectsFinder do describe 'filter by tags' do before do - public_project.tag_list.add('foo') + public_project.tag_list = 'foo' public_project.save! end diff --git a/spec/finders/repositories/branch_names_finder_spec.rb b/spec/finders/repositories/branch_names_finder_spec.rb index 4d8bfcc0f20..40f5d339832 100644 --- a/spec/finders/repositories/branch_names_finder_spec.rb +++ b/spec/finders/repositories/branch_names_finder_spec.rb @@ -5,21 +5,34 @@ require 'spec_helper' RSpec.describe Repositories::BranchNamesFinder do let(:project) { create(:project, :repository) } - let(:branch_names_finder) { described_class.new(project.repository, search: 'conflict-*') } - describe '#execute' do - subject(:execute) { branch_names_finder.execute } - - it 'filters branch names' do - expect(execute).to contain_exactly( - 'conflict-binary-file', - 'conflict-resolvable', - 'conflict-contains-conflict-markers', - 'conflict-missing-side', - 'conflict-start', - 'conflict-non-utf8', - 'conflict-too-large' + it 'returns all filtered branch names' do + expect(create_branch_names_finder(0, 100).execute).to contain_exactly( + 'snippet/edit-file', + 'snippet/multiple-files', + 'snippet/no-files', + 'snippet/rename-and-edit-file', + 'snippet/single-file' ) end + + it 'returns a limited number of offset filtered branch names' do + starting_names = create_branch_names_finder(0, 3).execute + offset_names = create_branch_names_finder(3, 2).execute + + expect(starting_names.count).to eq(3) + expect(offset_names.count).to eq(2) + + expect(offset_names).not_to include(*starting_names) + + all_names = create_branch_names_finder(0, 100).execute + expect(all_names).to contain_exactly(*starting_names, *offset_names) + end + + private + + def create_branch_names_finder(offset, limit) + described_class.new(project.repository, search: 'snippet/*', offset: offset, limit: limit) + end end end diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb index 164975fdfb6..b7339288c51 100644 --- a/spec/finders/template_finder_spec.rb +++ b/spec/finders/template_finder_spec.rb @@ -21,7 +21,6 @@ RSpec.describe TemplateFinder do :gitignores | 'Actionscript' :gitlab_ci_ymls | 'Android' :metrics_dashboard_ymls | 'Default' - :gitlab_ci_syntax_ymls | 'Artifacts example' end with_them do @@ -110,7 +109,6 @@ RSpec.describe TemplateFinder do :gitlab_ci_ymls | described_class :licenses | ::LicenseTemplateFinder :metrics_dashboard_ymls | described_class - :gitlab_ci_syntax_ymls | described_class :issues | described_class :merge_requests | described_class end @@ -160,7 +158,6 @@ RSpec.describe TemplateFinder do :gitignores | 'Actionscript' :gitlab_ci_ymls | 'Android' :metrics_dashboard_ymls | 'Default' - :gitlab_ci_syntax_ymls | 'Artifacts example' end with_them do diff --git a/spec/finders/users_with_pending_todos_finder_spec.rb b/spec/finders/users_with_pending_todos_finder_spec.rb deleted file mode 100644 index 565b65fbefe..00000000000 --- a/spec/finders/users_with_pending_todos_finder_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe UsersWithPendingTodosFinder do - describe '#execute' do - it 'returns the users for all pending todos of a target' do - issue = create(:issue) - note = create(:note) - todo = create(:todo, :pending, target: issue) - - create(:todo, :pending, target: note) - - users = described_class.new(issue).execute - - expect(users).to eq([todo.user]) - end - end -end |