diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-20 23:50:22 +0000 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/finders | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) | |
download | gitlab-ce-9dc93a4519d9d5d7be48ff274127136236a3adb3.tar.gz |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/finders')
22 files changed, 973 insertions, 396 deletions
diff --git a/spec/finders/applications_finder_spec.rb b/spec/finders/applications_finder_spec.rb index dc615144b88..b6c48d8cdae 100644 --- a/spec/finders/applications_finder_spec.rb +++ b/spec/finders/applications_finder_spec.rb @@ -5,18 +5,48 @@ require 'spec_helper' RSpec.describe ApplicationsFinder do let(:application1) { create(:application, name: 'some_application', owner: nil, redirect_uri: 'http://some_application.url', scopes: '') } let(:application2) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: '') } + let(:user_application) { create(:application, name: 'user_application', owner: create(:user), redirect_uri: 'http://user_application.url', scopes: '') } + let(:group_application) { create(:application, name: 'group_application', owner: create(:group), redirect_uri: 'http://group_application.url', scopes: '') } describe '#execute' do - it 'returns an array of applications' do + it 'returns an array of instance applications' do found = described_class.new.execute expect(found).to match_array([application1, application2]) end - it 'returns the application by id' do - params = { id: application1.id } - found = described_class.new(params).execute - expect(found).to match(application1) + context 'by_id' do + context 'with existing id' do + it 'returns the application' do + params = { id: application1.id } + found = described_class.new(params).execute + + expect(found).to match(application1) + end + end + + context 'with invalid id' do + it 'returns nil for user application' do + params = { id: user_application.id } + found = described_class.new(params).execute + + expect(found).to be_nil + end + + it 'returns nil for group application' do + params = { id: group_application.id } + found = described_class.new(params).execute + + expect(found).to be_nil + end + + it 'returns nil for non-existing application' do + params = { id: non_existing_record_id } + found = described_class.new(params).execute + + expect(found).to be_nil + end + end end end end diff --git a/spec/finders/ci/variables_finder_spec.rb b/spec/finders/ci/variables_finder_spec.rb index cd5f950ca8e..683788452cc 100644 --- a/spec/finders/ci/variables_finder_spec.rb +++ b/spec/finders/ci/variables_finder_spec.rb @@ -3,42 +3,57 @@ require 'spec_helper' RSpec.describe Ci::VariablesFinder do - let!(:project) { create(:project) } - let!(:params) { {} } + shared_examples 'scoped variables' do + describe '#initialize' do + subject { described_class.new(owner, params) } - let!(:var1) { create(:ci_variable, project: project, key: 'key1', environment_scope: 'staging') } - let!(:var2) { create(:ci_variable, project: project, key: 'key2', environment_scope: 'staging') } - let!(:var3) { create(:ci_variable, project: project, key: 'key2', environment_scope: 'production') } + context 'without key filter' do + let!(:params) { {} } - describe '#initialize' do - subject { described_class.new(project, params) } - - context 'without key filter' do - let!(:params) { {} } - - it 'raises an error' do - expect { subject }.to raise_error(ArgumentError, 'Please provide params[:key]') + it 'raises an error' do + expect { subject }.to raise_error(ArgumentError, 'Please provide params[:key]') + end end end - end - describe '#execute' do - subject { described_class.new(project.reload, params).execute } + describe '#execute' do + subject { described_class.new(owner.reload, params).execute } - context 'with key filter' do - let!(:params) { { key: 'key1' } } + context 'with key filter' do + let!(:params) { { key: 'key1' } } - it 'returns var1' do - expect(subject).to contain_exactly(var1) + it 'returns var1' do + expect(subject).to contain_exactly(var1) + end end - end - context 'with key and environment_scope filter' do - let!(:params) { { key: 'key2', filter: { environment_scope: 'staging' } } } + context 'with key and environment_scope filter' do + let!(:params) { { key: 'key2', filter: { environment_scope: 'staging' } } } - it 'returns var2' do - expect(subject).to contain_exactly(var2) + it 'returns var2' do + expect(subject).to contain_exactly(var2) + end end end end + + context 'for a project' do + let(:owner) { create(:project) } + + let!(:var1) { create(:ci_variable, project: owner, key: 'key1', environment_scope: 'staging') } + let!(:var2) { create(:ci_variable, project: owner, key: 'key2', environment_scope: 'staging') } + let!(:var3) { create(:ci_variable, project: owner, key: 'key2', environment_scope: 'production') } + + include_examples 'scoped variables' + end + + context 'for a group' do + let(:owner) { create(:group) } + + let!(:var1) { create(:ci_group_variable, group: owner, key: 'key1', environment_scope: 'staging') } + let!(:var2) { create(:ci_group_variable, group: owner, key: 'key2', environment_scope: 'staging') } + let!(:var3) { create(:ci_group_variable, group: owner, key: 'key2', environment_scope: 'production') } + + include_examples 'scoped variables' + end end diff --git a/spec/finders/concerns/finder_with_group_hierarchy_spec.rb b/spec/finders/concerns/finder_with_group_hierarchy_spec.rb new file mode 100644 index 00000000000..8c2026a00a1 --- /dev/null +++ b/spec/finders/concerns/finder_with_group_hierarchy_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe FinderWithGroupHierarchy do + let(:finder_class) do + Class.new do + include FinderWithGroupHierarchy + include Gitlab::Utils::StrongMemoize + + def initialize(current_user, params = {}) + @current_user = current_user + @params = params + end + + def execute(skip_authorization: false) + @skip_authorization = skip_authorization + + item_ids + end + + # normally an array of item ids would be returned, + # however for this spec just return the group ids + def item_ids + group? ? group_ids_for(group) : [] + end + + private + + attr_reader :current_user, :params, :skip_authorization + + def read_permission + :read_label + end + end + end + + let_it_be(:parent_group) { create(:group) } + let_it_be(:group) { create(:group, parent: parent_group) } + let_it_be(:private_group) { create(:group, :private) } + let_it_be(:private_subgroup) { create(:group, :private, parent: private_group) } + + let(:user) { create(:user) } + + context 'when specifying group' do + it 'returns only the group by default' do + finder = finder_class.new(user, group: group) + + expect(finder.execute).to match_array([group.id]) + end + end + + context 'when specifying group_id' do + it 'returns only the group by default' do + finder = finder_class.new(user, group_id: group.id) + + expect(finder.execute).to match_array([group.id]) + end + end + + context 'when including items from group ancestors' do + before do + private_subgroup.add_developer(user) + end + + it 'returns group and its ancestors' do + private_group.add_developer(user) + + finder = finder_class.new(user, group: private_subgroup, include_ancestor_groups: true) + + expect(finder.execute).to match_array([private_group.id, private_subgroup.id]) + end + + it 'ignores groups which user can not read' do + finder = finder_class.new(user, group: private_subgroup, include_ancestor_groups: true) + + expect(finder.execute).to match_array([private_subgroup.id]) + end + + it 'returns them all when skip_authorization is true' do + finder = finder_class.new(user, group: private_subgroup, include_ancestor_groups: true) + + expect(finder.execute(skip_authorization: true)).to match_array([private_group.id, private_subgroup.id]) + end + end + + context 'when including items from group descendants' do + before do + private_subgroup.add_developer(user) + end + + it 'returns items from group and its descendants' do + private_group.add_developer(user) + + finder = finder_class.new(user, group: private_group, include_descendant_groups: true) + + expect(finder.execute).to match_array([private_group.id, private_subgroup.id]) + end + + it 'ignores items from groups which user can not read' do + finder = finder_class.new(user, group: private_group, include_descendant_groups: true) + + expect(finder.execute).to match_array([private_subgroup.id]) + end + + it 'returns them all when skip_authorization is true' do + finder = finder_class.new(user, group: private_group, include_descendant_groups: true) + + expect(finder.execute(skip_authorization: true)).to match_array([private_group.id, private_subgroup.id]) + end + end +end diff --git a/spec/finders/concerns/packages/finder_helper_spec.rb b/spec/finders/concerns/packages/finder_helper_spec.rb index 73f77647573..c1740ee1796 100644 --- a/spec/finders/concerns/packages/finder_helper_spec.rb +++ b/spec/finders/concerns/packages/finder_helper_spec.rb @@ -6,7 +6,6 @@ RSpec.describe ::Packages::FinderHelper do describe '#packages_visible_to_user' do using RSpec::Parameterized::TableSyntax - let_it_be(:user) { create(:user) } let_it_be_with_reload(:group) { create(:group) } let_it_be_with_reload(:project1) { create(:project, namespace: group) } let_it_be(:package1) { create(:package, project: project1) } @@ -44,41 +43,87 @@ RSpec.describe ::Packages::FinderHelper do it { is_expected.to be_empty } end - where(:group_visibility, :subgroup_visibility, :project2_visibility, :user_role, :shared_example_name) do - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :maintainer | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :developer | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :guest | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :anonymous | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :maintainer | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :developer | 'returning both packages' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :guest | 'returning package1' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :anonymous | 'returning package1' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both packages' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both packages' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning package1' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning package1' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both packages' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both packages' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning no packages' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning no packages' + context 'with a user' do + let_it_be(:user) { create(:user) } + + where(:group_visibility, :subgroup_visibility, :project2_visibility, :user_role, :shared_example_name) do + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :maintainer | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :developer | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :guest | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :anonymous | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :maintainer | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :developer | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :guest | 'returning package1' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :anonymous | 'returning package1' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both packages' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both packages' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning package1' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning package1' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both packages' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both packages' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning no packages' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning no packages' + end + + with_them do + before do + unless user_role == :anonymous + group.send("add_#{user_role}", user) + subgroup.send("add_#{user_role}", user) + project1.send("add_#{user_role}", user) + project2.send("add_#{user_role}", user) + end + + project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) + subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) + project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + end + + it_behaves_like params[:shared_example_name] + end end - with_them do - before do - unless user_role == :anonymous - group.send("add_#{user_role}", user) - subgroup.send("add_#{user_role}", user) - project1.send("add_#{user_role}", user) - project2.send("add_#{user_role}", user) + context 'with a group deploy token' do + let_it_be(:user) { create(:deploy_token, :group, read_package_registry: true) } + let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) } + + shared_examples 'handling all conditions' do + where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both packages' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both packages' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both packages' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both packages' + end + + with_them do + before do + project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) + subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) + project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + end + + it_behaves_like params[:shared_example_name] + end + end + + context 'with packages_finder_helper_deploy_token enabled' do + before do + expect(group).not_to receive(:all_projects) end - project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) - subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) - project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + it_behaves_like 'handling all conditions' end - it_behaves_like params[:shared_example_name] + context 'with packages_finder_helper_deploy_token disabled' do + before do + stub_feature_flags(packages_finder_helper_deploy_token: false) + expect(group).to receive(:all_projects).and_call_original + end + + it_behaves_like 'handling all conditions' + end end end @@ -121,41 +166,87 @@ RSpec.describe ::Packages::FinderHelper do it { is_expected.to be_empty } end - where(:group_visibility, :subgroup_visibility, :project2_visibility, :user_role, :shared_example_name) do - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :maintainer | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :developer | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :guest | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :anonymous | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :maintainer | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :developer | 'returning both projects' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :guest | 'returning project1' - 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :anonymous | 'returning project1' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both projects' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both projects' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning project1' - 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning project1' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both projects' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both projects' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning no project' - 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning no project' + context 'with a user' do + let_it_be(:user) { create(:user) } + + where(:group_visibility, :subgroup_visibility, :project2_visibility, :user_role, :shared_example_name) do + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :maintainer | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :developer | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :guest | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | :anonymous | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :maintainer | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :developer | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :guest | 'returning project1' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | :anonymous | 'returning project1' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both projects' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both projects' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning project1' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning project1' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :maintainer | 'returning both projects' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :developer | 'returning both projects' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :guest | 'returning no project' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | :anonymous | 'returning no project' + end + + with_them do + before do + unless user_role == :anonymous + group.send("add_#{user_role}", user) + subgroup.send("add_#{user_role}", user) + project1.send("add_#{user_role}", user) + project2.send("add_#{user_role}", user) + end + + project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) + subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) + project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + end + + it_behaves_like params[:shared_example_name] + end end - with_them do - before do - unless user_role == :anonymous - group.send("add_#{user_role}", user) - subgroup.send("add_#{user_role}", user) - project1.send("add_#{user_role}", user) - project2.send("add_#{user_role}", user) + context 'with a group deploy token' do + let_it_be(:user) { create(:deploy_token, :group, read_package_registry: true) } + let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) } + + shared_examples 'handling all conditions' do + where(:group_visibility, :subgroup_visibility, :project2_visibility, :shared_example_name) do + 'PUBLIC' | 'PUBLIC' | 'PUBLIC' | 'returning both projects' + 'PUBLIC' | 'PUBLIC' | 'PRIVATE' | 'returning both projects' + 'PUBLIC' | 'PRIVATE' | 'PRIVATE' | 'returning both projects' + 'PRIVATE' | 'PRIVATE' | 'PRIVATE' | 'returning both projects' end - project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) - subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) - project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + with_them do + before do + project2.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project2_visibility, false)) + subgroup.update!(visibility_level: Gitlab::VisibilityLevel.const_get(subgroup_visibility, false)) + project1.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_visibility, false)) + end + + it_behaves_like params[:shared_example_name] + end end - it_behaves_like params[:shared_example_name] + context 'with packages_finder_helper_deploy_token enabled' do + before do + expect(group).not_to receive(:all_projects) + end + + it_behaves_like 'handling all conditions' + end + + context 'with packages_finder_helper_deploy_token disabled' do + before do + stub_feature_flags(packages_finder_helper_deploy_token: false) + expect(group).to receive(:all_projects).and_call_original + end + + it_behaves_like 'handling all conditions' + end end end end diff --git a/spec/finders/design_management/designs_finder_spec.rb b/spec/finders/design_management/designs_finder_spec.rb index feb78a4bc4b..631f23b7312 100644 --- a/spec/finders/design_management/designs_finder_spec.rb +++ b/spec/finders/design_management/designs_finder_spec.rb @@ -11,6 +11,7 @@ RSpec.describe DesignManagement::DesignsFinder do let_it_be(:design1) { create(:design, :with_file, issue: issue, versions_count: 1, relative_position: 3) } let_it_be(:design2) { create(:design, :with_file, issue: issue, versions_count: 1, relative_position: 2) } let_it_be(:design3) { create(:design, :with_file, issue: issue, versions_count: 1, relative_position: 1) } + let(:params) { {} } subject(:designs) { described_class.new(issue, user, params).execute } diff --git a/spec/finders/design_management/versions_finder_spec.rb b/spec/finders/design_management/versions_finder_spec.rb index 6a56ccb10b8..0d606ef46f1 100644 --- a/spec/finders/design_management/versions_finder_spec.rb +++ b/spec/finders/design_management/versions_finder_spec.rb @@ -10,6 +10,7 @@ RSpec.describe DesignManagement::VersionsFinder do let_it_be(:issue) { create(:issue, project: project) } let_it_be(:design_1) { create(:design, :with_file, issue: issue, versions_count: 1) } let_it_be(:design_2) { create(:design, :with_file, issue: issue, versions_count: 1) } + let(:version_1) { design_1.versions.first } let(:version_2) { design_2.versions.first } let(:design_or_collection) { issue.design_collection } diff --git a/spec/finders/environments_by_deployments_finder_spec.rb b/spec/finders/environments_by_deployments_finder_spec.rb new file mode 100644 index 00000000000..f5fcc4ef72a --- /dev/null +++ b/spec/finders/environments_by_deployments_finder_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe EnvironmentsByDeploymentsFinder do + let(:project) { create(:project, :repository) } + let(:user) { project.creator } + let(:environment) { create(:environment, :available, project: project) } + + before do + project.add_maintainer(user) + end + + describe '#execute' do + context 'tagged deployment' do + let(:environment_two) { create(:environment, project: project) } + # Environments need to include commits, so rewind two commits to fit + let(:commit) { project.commit('HEAD~2') } + + before do + create(:deployment, :success, environment: environment, ref: 'v1.0.0', tag: true, sha: project.commit.id) + create(:deployment, :success, environment: environment_two, ref: 'v1.1.0', tag: true, sha: project.commit('HEAD~1').id) + end + + it 'returns environment when with_tags is set' do + expect(described_class.new(project, user, ref: 'master', commit: commit, with_tags: true).execute) + .to contain_exactly(environment, environment_two) + end + + it 'does not return environment when no with_tags is set' do + expect(described_class.new(project, user, ref: 'master', commit: commit).execute) + .to be_empty + end + + it 'does not return environment when commit is not part of deployment' do + expect(described_class.new(project, user, ref: 'master', commit: project.commit('feature')).execute) + .to be_empty + end + + # We expect two Gitaly calls: FindCommit, CommitIsAncestor + # This tests to ensure we don't call one CommitIsAncestor per environment + it 'only calls Gitaly twice when multiple environments are present', :request_store do + expect do + result = described_class.new(project, user, ref: 'master', commit: commit, with_tags: true, find_latest: true).execute + + expect(result).to contain_exactly(environment_two) + end.to change { Gitlab::GitalyClient.get_request_count }.by(2) + end + end + + context 'branch deployment' do + before do + create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id) + end + + it 'returns environment when ref is set' do + expect(described_class.new(project, user, ref: 'master', commit: project.commit).execute) + .to contain_exactly(environment) + end + + it 'does not environment when ref is different' do + expect(described_class.new(project, user, ref: 'feature', commit: project.commit).execute) + .to be_empty + end + + it 'does not return environment when commit is not part of deployment' do + expect(described_class.new(project, user, ref: 'master', commit: project.commit('feature')).execute) + .to be_empty + end + + it 'returns environment when commit constraint is not set' do + expect(described_class.new(project, user, ref: 'master').execute) + .to contain_exactly(environment) + end + end + + context 'commit deployment' do + before do + create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id) + end + + it 'returns environment' do + expect(described_class.new(project, user, commit: project.commit).execute) + .to contain_exactly(environment) + end + end + + context 'recently updated' do + context 'when last deployment to environment is the most recent one' do + before do + create(:deployment, :success, environment: environment, ref: 'feature') + end + + it 'finds recently updated environment' do + expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) + .to contain_exactly(environment) + end + end + + context 'when last deployment to environment is not the most recent' do + before do + create(:deployment, :success, environment: environment, ref: 'feature') + create(:deployment, :success, environment: environment, ref: 'master') + end + + it 'does not find environment' do + expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) + .to be_empty + end + end + + context 'when there are two environments that deploy to the same branch' do + let(:second_environment) { create(:environment, project: project) } + + before do + create(:deployment, :success, environment: environment, ref: 'feature') + create(:deployment, :success, environment: second_environment, ref: 'feature') + end + + it 'finds both environments' do + expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) + .to contain_exactly(environment, second_environment) + end + end + end + end +end diff --git a/spec/finders/environments_finder_spec.rb b/spec/finders/environments_finder_spec.rb index fd714ab9a8f..c2022331ad9 100644 --- a/spec/finders/environments_finder_spec.rb +++ b/spec/finders/environments_finder_spec.rb @@ -12,150 +12,36 @@ RSpec.describe EnvironmentsFinder do end describe '#execute' do - context 'tagged deployment' do - let(:environment_two) { create(:environment, project: project) } - # Environments need to include commits, so rewind two commits to fit - let(:commit) { project.commit('HEAD~2') } - - before do - create(:deployment, :success, environment: environment, ref: 'v1.0.0', tag: true, sha: project.commit.id) - create(:deployment, :success, environment: environment_two, ref: 'v1.1.0', tag: true, sha: project.commit('HEAD~1').id) - end - - it 'returns environment when with_tags is set' do - expect(described_class.new(project, user, ref: 'master', commit: commit, with_tags: true).execute) - .to contain_exactly(environment, environment_two) - end - - it 'does not return environment when no with_tags is set' do - expect(described_class.new(project, user, ref: 'master', commit: commit).execute) - .to be_empty - end - - it 'does not return environment when commit is not part of deployment' do - expect(described_class.new(project, user, ref: 'master', commit: project.commit('feature')).execute) - .to be_empty - end - - # We expect two Gitaly calls: FindCommit, CommitIsAncestor - # This tests to ensure we don't call one CommitIsAncestor per environment - it 'only calls Gitaly twice when multiple environments are present', :request_store do - expect do - result = described_class.new(project, user, ref: 'master', commit: commit, with_tags: true, find_latest: true).execute - - expect(result).to contain_exactly(environment_two) - end.to change { Gitlab::GitalyClient.get_request_count }.by(2) - end - end - - context 'branch deployment' do - before do - create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id) - end - - it 'returns environment when ref is set' do - expect(described_class.new(project, user, ref: 'master', commit: project.commit).execute) - .to contain_exactly(environment) - end - - it 'does not environment when ref is different' do - expect(described_class.new(project, user, ref: 'feature', commit: project.commit).execute) - .to be_empty - end - - it 'does not return environment when commit is not part of deployment' do - expect(described_class.new(project, user, ref: 'master', commit: project.commit('feature')).execute) - .to be_empty - end - - it 'returns environment when commit constraint is not set' do - expect(described_class.new(project, user, ref: 'master').execute) - .to contain_exactly(environment) - end - end - - context 'commit deployment' do - before do - create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id) - end - - it 'returns environment' do - expect(described_class.new(project, user, commit: project.commit).execute) - .to contain_exactly(environment) - end - end - - context 'recently updated' do - context 'when last deployment to environment is the most recent one' do - before do - create(:deployment, :success, environment: environment, ref: 'feature') - end - - it 'finds recently updated environment' do - expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) - .to contain_exactly(environment) - end - end - - context 'when last deployment to environment is not the most recent' do - before do - create(:deployment, :success, environment: environment, ref: 'feature') - create(:deployment, :success, environment: environment, ref: 'master') - end - - it 'does not find environment' do - expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) - .to be_empty - end - end - - context 'when there are two environments that deploy to the same branch' do - let(:second_environment) { create(:environment, project: project) } - - before do - create(:deployment, :success, environment: environment, ref: 'feature') - create(:deployment, :success, environment: second_environment, ref: 'feature') - end - - it 'finds both environments' do - expect(described_class.new(project, user, ref: 'feature', recently_updated: true).execute) - .to contain_exactly(environment, second_environment) - end - end - end - end - - describe '#find' do context 'with states parameter' do let(:stopped_environment) { create(:environment, :stopped, project: project) } it 'returns environments with the requested state' do - result = described_class.new(project, user, states: 'available').find + result = described_class.new(project, user, states: 'available').execute expect(result).to contain_exactly(environment) end it 'returns environments with any of the requested states' do - result = described_class.new(project, user, states: %w(available stopped)).find + result = described_class.new(project, user, states: %w(available stopped)).execute expect(result).to contain_exactly(environment, stopped_environment) end it 'raises exception when requested state is invalid' do - expect { described_class.new(project, user, states: %w(invalid stopped)).find }.to( + expect { described_class.new(project, user, states: %w(invalid stopped)).execute }.to( raise_error(described_class::InvalidStatesError, 'Requested states are invalid') ) end context 'works with symbols' do it 'returns environments with the requested state' do - result = described_class.new(project, user, states: :available).find + result = described_class.new(project, user, states: :available).execute expect(result).to contain_exactly(environment) end it 'returns environments with any of the requested states' do - result = described_class.new(project, user, states: [:available, :stopped]).find + result = described_class.new(project, user, states: [:available, :stopped]).execute expect(result).to contain_exactly(environment, stopped_environment) end @@ -167,7 +53,7 @@ RSpec.describe EnvironmentsFinder do let(:environment3) { create(:environment, :available, name: 'test3', project: project) } it 'searches environments by name and state' do - result = described_class.new(project, user, search: 'test', states: :available).find + result = described_class.new(project, user, search: 'test', states: :available).execute expect(result).to contain_exactly(environment3) end diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index a87a05d4408..3238f6744f7 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -3,174 +3,180 @@ require 'spec_helper' RSpec.describe GroupMembersFinder, '#execute' do - let(:group) { create(:group) } - let(:nested_group) { create(:group, parent: group) } - let(:deeper_nested_group) { create(:group, parent: nested_group) } - let(:user1) { create(:user) } - let(:user2) { create(:user) } - let(:user3) { create(:user) } - let(:user4) { create(:user) } - let(:user5) { create(:user, :two_factor_via_otp) } - - it 'returns members for top-level group' do - member1 = group.add_maintainer(user1) - member2 = group.add_maintainer(user2) - member3 = group.add_maintainer(user3) - create(:group_member, :minimal_access, user: create(:user), source: group) - - result = described_class.new(group).execute - - expect(result.to_a).to match_array([member3, member2, member1]) + let(:group) { create(:group) } + let(:sub_group) { create(:group, parent: group) } + let(:sub_sub_group) { create(:group, parent: sub_group) } + let(:user1) { create(:user) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + let(:user4) { create(:user) } + let(:user5) { create(:user, :two_factor_via_otp) } + + let(:groups) do + { + group: group, + sub_group: sub_group, + sub_sub_group: sub_sub_group + } end - it 'returns members & inherited members for nested group by default' do - group.add_developer(user2) - nested_group.request_access(user4) - member1 = group.add_maintainer(user1) - member3 = nested_group.add_maintainer(user2) - member4 = nested_group.add_maintainer(user3) - - result = described_class.new(nested_group).execute - - expect(result.to_a).to match_array([member1, member3, member4]) + context 'relations' do + let!(:members) do + { + user1_sub_sub_group: create(:group_member, :maintainer, group: sub_sub_group, user: user1), + user1_sub_group: create(:group_member, :developer, group: sub_group, user: user1), + user1_group: create(:group_member, :reporter, group: group, user: user1), + user2_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user2), + user2_sub_group: create(:group_member, :developer, group: sub_group, user: user2), + user2_group: create(:group_member, :maintainer, group: group, user: user2), + user3_sub_sub_group: create(:group_member, :developer, group: sub_sub_group, user: user3, expires_at: 1.day.from_now), + user3_sub_group: create(:group_member, :developer, group: sub_group, user: user3, expires_at: 2.days.from_now), + user3_group: create(:group_member, :reporter, group: group, user: user3), + user4_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user4), + user4_sub_group: create(:group_member, :developer, group: sub_group, user: user4, expires_at: 1.day.from_now), + user4_group: create(:group_member, :developer, group: group, user: user4, expires_at: 2.days.from_now) + } + end + + using RSpec::Parameterized::TableSyntax + + where(:subject_relations, :subject_group, :expected_members) do + nil | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:direct] | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:inherited] | :group | [] + [:descendants] | :group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:direct, :inherited] | :group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:direct, :descendants] | :group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:descendants, :inherited] | :group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:direct, :descendants, :inherited] | :group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + nil | :sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct] | :sub_group | [:user1_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:inherited] | :sub_group | [:user1_group, :user2_group, :user3_group, :user4_group] + [:descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:direct, :inherited] | :sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group] + [:descendants, :inherited] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_sub_group, :user4_group] + [:direct, :descendants, :inherited] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + nil | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:descendants] | :sub_sub_group | [] + [:direct, :inherited] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group] + [:descendants, :inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group] + [:direct, :descendants, :inherited] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group] + end + + with_them do + it 'returns correct members' do + result = if subject_relations + described_class.new(groups[subject_group]).execute(include_relations: subject_relations) + else + described_class.new(groups[subject_group]).execute + end + + expect(result.to_a).to match_array(expected_members.map { |name| members[name] }) + end + end end - it 'does not return inherited members for nested group if requested' do - group.add_maintainer(user1) - group.add_developer(user2) - member2 = nested_group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) + context 'search' do + it 'returns searched members if requested' do + group.add_maintainer(user2) + group.add_developer(user3) + member = group.add_maintainer(user1) - result = described_class.new(nested_group).execute(include_relations: [:direct]) + result = described_class.new(group, params: { search: user1.name }).execute - expect(result.to_a).to match_array([member2, member3]) - end + expect(result.to_a).to match_array([member]) + end - it 'returns only inherited members for nested group if requested' do - group.add_developer(user2) - nested_group.request_access(user4) - member1 = group.add_maintainer(user1) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) + it 'returns nothing if search only in inherited relation' do + group.add_maintainer(user2) + group.add_developer(user3) + group.add_maintainer(user1) - result = described_class.new(nested_group).execute(include_relations: [:inherited]) + result = described_class.new(group, params: { search: user1.name }).execute(include_relations: [:inherited]) - expect(result.to_a).to match_array([member1]) - end + expect(result.to_a).to match_array([]) + end - it 'does not return nil if `inherited only` relation is requested on root group' do - group.add_developer(user2) + it 'returns searched member only from sub_group if search only in inherited relation' do + group.add_maintainer(user2) + group.add_developer(user3) + sub_group.add_maintainer(create(:user, name: user1.name)) + member = group.add_maintainer(user1) - result = described_class.new(group).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, params: { search: member.user.name }).execute(include_relations: [:inherited]) - expect(result).not_to be_nil + expect(result.to_a).to contain_exactly(member) + end end - it 'returns members for descendant groups if requested' do - member1 = group.add_maintainer(user2) - member2 = group.add_maintainer(user1) - nested_group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) - member4 = nested_group.add_maintainer(user4) + context 'filter by two-factor' do + it 'returns members with two-factor auth if requested by owner' do + group.add_owner(user2) + group.add_maintainer(user1) + member = group.add_maintainer(user5) - result = described_class.new(group).execute(include_relations: [:direct, :descendants]) + result = described_class.new(group, user2, params: { two_factor: 'enabled' }).execute - expect(result.to_a).to match_array([member1, member2, member3, member4]) - end + expect(result.to_a).to contain_exactly(member) + end - it 'returns searched members if requested' do - group.add_maintainer(user2) - group.add_developer(user3) - member = group.add_maintainer(user1) + it 'returns members without two-factor auth if requested by owner' do + member1 = group.add_owner(user2) + member2 = group.add_maintainer(user1) + member_with_2fa = group.add_maintainer(user5) - result = described_class.new(group, params: { search: user1.name }).execute + result = described_class.new(group, user2, params: { two_factor: 'disabled' }).execute - expect(result.to_a).to match_array([member]) - end + expect(result.to_a).not_to include(member_with_2fa) + expect(result.to_a).to match_array([member1, member2]) + end - it 'returns nothing if search only in inherited relation' do - group.add_maintainer(user2) - group.add_developer(user3) - group.add_maintainer(user1) + it 'returns direct members with two-factor auth if requested by owner' do + group.add_owner(user1) + group.add_maintainer(user2) + sub_group.add_maintainer(user3) + member_with_2fa = sub_group.add_maintainer(user5) - result = described_class.new(group, params: { search: user1.name }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:direct]) - expect(result.to_a).to match_array([]) - end + expect(result.to_a).to match_array([member_with_2fa]) + end - it 'returns searched member only from nested_group if search only in inherited relation' do - group.add_maintainer(user2) - group.add_developer(user3) - nested_group.add_maintainer(create(:user, name: user1.name)) - member = group.add_maintainer(user1) + it 'returns inherited members with two-factor auth if requested by owner' do + group.add_owner(user1) + member_with_2fa = group.add_maintainer(user5) + sub_group.add_maintainer(user2) + sub_group.add_maintainer(user3) - result = described_class.new(nested_group, params: { search: member.user.name }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:inherited]) - expect(result.to_a).to contain_exactly(member) - end - - it 'returns members with two-factor auth if requested by owner' do - group.add_owner(user2) - group.add_maintainer(user1) - member = group.add_maintainer(user5) - - result = described_class.new(group, user2, params: { two_factor: 'enabled' }).execute + expect(result.to_a).to match_array([member_with_2fa]) + end - expect(result.to_a).to contain_exactly(member) - end - - it 'returns members without two-factor auth if requested by owner' do - member1 = group.add_owner(user2) - member2 = group.add_maintainer(user1) - member_with_2fa = group.add_maintainer(user5) + it 'returns direct members without two-factor auth if requested by owner' do + group.add_owner(user1) + group.add_maintainer(user2) + member3 = sub_group.add_maintainer(user3) + sub_group.add_maintainer(user5) - result = described_class.new(group, user2, params: { two_factor: 'disabled' }).execute + result = described_class.new(sub_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:direct]) - expect(result.to_a).not_to include(member_with_2fa) - expect(result.to_a).to match_array([member1, member2]) - end - - it 'returns direct members with two-factor auth if requested by owner' do - group.add_owner(user1) - group.add_maintainer(user2) - nested_group.add_maintainer(user3) - member_with_2fa = nested_group.add_maintainer(user5) - - result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:direct]) - - expect(result.to_a).to match_array([member_with_2fa]) - end - - it 'returns inherited members with two-factor auth if requested by owner' do - group.add_owner(user1) - member_with_2fa = group.add_maintainer(user5) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) - - result = described_class.new(nested_group, user1, params: { two_factor: 'enabled' }).execute(include_relations: [:inherited]) - - expect(result.to_a).to match_array([member_with_2fa]) - end - - it 'returns direct members without two-factor auth if requested by owner' do - group.add_owner(user1) - group.add_maintainer(user2) - member3 = nested_group.add_maintainer(user3) - nested_group.add_maintainer(user5) - - result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:direct]) - - expect(result.to_a).to match_array([member3]) - end + expect(result.to_a).to match_array([member3]) + end - it 'returns inherited members without two-factor auth if requested by owner' do - member1 = group.add_owner(user1) - group.add_maintainer(user5) - nested_group.add_maintainer(user2) - nested_group.add_maintainer(user3) + it 'returns inherited members without two-factor auth if requested by owner' do + member1 = group.add_owner(user1) + group.add_maintainer(user5) + sub_group.add_maintainer(user2) + sub_group.add_maintainer(user3) - result = described_class.new(nested_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:inherited]) + result = described_class.new(sub_group, user1, params: { two_factor: 'disabled' }).execute(include_relations: [:inherited]) - expect(result.to_a).to match_array([member1]) + expect(result.to_a).to match_array([member1]) + end end end diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index b794ab626bf..a2aac857bf5 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -49,6 +49,13 @@ RSpec.describe IssuesFinder do let(:expected_issuables) { [issue3, issue4] } end + context 'when assignee_id does not exist' do + it_behaves_like 'assignee NOT ID filter' do + let(:params) { { not: { assignee_id: -100 } } } + let(:expected_issuables) { [issue1, issue2, issue3, issue4, issue5] } + end + end + context 'filter by username' do let_it_be(:user3) { create(:user) } @@ -71,6 +78,17 @@ RSpec.describe IssuesFinder do let(:params) { { not: { assignee_username: [user.username, user2.username] } } } let(:expected_issuables) { [issue3, issue4] } end + + context 'when assignee_username does not exist' do + it_behaves_like 'assignee NOT username filter' do + before do + issue2.assignees = [user2] + end + + let(:params) { { not: { assignee_username: 'non_existent_username' } } } + let(:expected_issuables) { [issue1, issue2, issue3, issue4, issue5] } + end + end end it_behaves_like 'no assignee filter' do diff --git a/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb index 4724a8eb5c7..6dffaff294d 100644 --- a/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb +++ b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb @@ -77,6 +77,45 @@ RSpec.describe MergeRequests::OldestPerCommitFinder do expect(described_class.new(project).execute(commits)).to eq(sha => mr) end + it 'includes a merge request that was squashed into the target branch' do + project = create(:project) + sha = Digest::SHA1.hexdigest('foo') + mr = create( + :merge_request, + :merged, + target_project: project, + squash_commit_sha: sha + ) + + commits = [double(:commit, id: sha)] + + expect(MergeRequestDiffCommit) + .not_to receive(:oldest_merge_request_id_per_commit) + + expect(described_class.new(project).execute(commits)).to eq(sha => mr) + end + + it 'includes a merge request for both a squash and merge commit' do + project = create(:project) + sha1 = Digest::SHA1.hexdigest('foo') + sha2 = Digest::SHA1.hexdigest('bar') + mr = create( + :merge_request, + :merged, + target_project: project, + squash_commit_sha: sha1, + merge_commit_sha: sha2 + ) + + commits = [double(:commit1, id: sha1), double(:commit2, id: sha2)] + + expect(MergeRequestDiffCommit) + .not_to receive(:oldest_merge_request_id_per_commit) + + expect(described_class.new(project).execute(commits)) + .to eq(sha1 => mr, sha2 => mr) + end + it 'includes the oldest merge request when a merge commit is present in a newer merge request' do project = create(:project) sha = Digest::SHA1.hexdigest('foo') diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index b3000498bb6..597d22801ca 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -156,6 +156,18 @@ RSpec.describe MergeRequestsFinder do it { is_expected.to eq([merge_request2]) } end + + context 'when project_id is given' do + subject(:query) { described_class.new(user, merged_after: 15.days.ago, merged_before: 6.days.ago, project_id: merge_request2.project).execute } + + it { is_expected.to eq([merge_request2]) } + + it 'queries merge_request_metrics.target_project_id table' do + expect(query.to_sql).to include(%{"merge_request_metrics"."target_project_id" = #{merge_request2.target_project_id}}) + + expect(query.to_sql).not_to include(%{"merge_requests"."target_project_id"}) + end + end end context 'filtering by group' do diff --git a/spec/finders/metrics/dashboards/annotations_finder_spec.rb b/spec/finders/metrics/dashboards/annotations_finder_spec.rb index 223fd2c047c..7c5932dde1e 100644 --- a/spec/finders/metrics/dashboards/annotations_finder_spec.rb +++ b/spec/finders/metrics/dashboards/annotations_finder_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Metrics::Dashboards::AnnotationsFinder do subject(:annotations) { described_class.new(dashboard: dashboard, params: params).execute } let_it_be(:current_user) { create(:user) } + let(:path) { 'config/prometheus/common_metrics.yml' } let(:params) { {} } let(:environment) { create(:environment) } diff --git a/spec/finders/metrics/users_starred_dashboards_finder_spec.rb b/spec/finders/metrics/users_starred_dashboards_finder_spec.rb index 61dadb5239c..4136cf1123a 100644 --- a/spec/finders/metrics/users_starred_dashboards_finder_spec.rb +++ b/spec/finders/metrics/users_starred_dashboards_finder_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Metrics::UsersStarredDashboardsFinder do subject(:starred_dashboards) { described_class.new(user: user, project: project, params: params).execute } let_it_be(:user) { create(:user) } + let(:project) { create(:project) } let(:dashboard_path) { 'config/prometheus/common_metrics.yml' } let(:params) { {} } diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index 868b126dc28..11de19cfdbc 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -213,6 +213,24 @@ RSpec.describe NotesFinder do expect { described_class.new(user, params).execute }.to raise_error(RuntimeError) end end + + describe 'sorting' do + it 'allows sorting' do + params = { project: project, sort: 'id_desc' } + + expect(Note).to receive(:order_id_desc).once + + described_class.new(user, params).execute + end + + it 'defaults to sort by .fresh' do + params = { project: project } + + expect(Note).to receive(:fresh).once + + described_class.new(user, params).execute + end + end end describe '.search' do diff --git a/spec/finders/packages/go/package_finder_spec.rb b/spec/finders/packages/go/package_finder_spec.rb new file mode 100644 index 00000000000..b6fad1e7061 --- /dev/null +++ b/spec/finders/packages/go/package_finder_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Go::PackageFinder do + include_context 'basic Go module' + + 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(:finder) { described_class.new(project, mod_name, version_name) } + + describe '#exists?' do + subject { finder.exists? } + + context 'with a valid name and version' do + let(:mod_name) { mod.name } + let(:version_name) { version.name } + + it 'executes SELECT 1' do + expect { subject }.to exceed_query_limit(0).for_query(/^SELECT 1/) + end + + it { is_expected.to eq(true) } + end + + context 'with an invalid name' do + let(:mod_name) { 'foo/bar' } + let(:version_name) { 'baz' } + + it { is_expected.to eq(false) } + end + + context 'with an invalid version' do + let(:mod_name) { mod.name } + let(:version_name) { 'baz' } + + it { is_expected.to eq(false) } + end + end + + describe '#execute' do + subject { finder.execute } + + context 'with a valid name and version' do + let(:mod_name) { mod.name } + let(:version_name) { version.name } + + it 'executes a single query' do + expect { subject }.not_to exceed_query_limit(1) + end + + it { is_expected.to eq(package) } + end + + context 'with an invalid name' do + let(:mod_name) { 'foo/bar' } + let(:version_name) { 'baz' } + + it { is_expected.to eq(nil) } + end + + context 'with an invalid version' do + let(:mod_name) { mod.name } + let(:version_name) { 'baz' } + + it { is_expected.to eq(nil) } + end + end +end diff --git a/spec/finders/packages/maven/package_finder_spec.rb b/spec/finders/packages/maven/package_finder_spec.rb index b955c331f28..ca144292501 100644 --- a/spec/finders/packages/maven/package_finder_spec.rb +++ b/spec/finders/packages/maven/package_finder_spec.rb @@ -11,71 +11,144 @@ RSpec.describe ::Packages::Maven::PackageFinder do let(:param_path) { nil } let(:param_project) { nil } let(:param_group) { nil } - let(:finder) { described_class.new(param_path, user, project: param_project, group: param_group) } + 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) } before do group.add_developer(user) end - describe '#execute!' do - subject { finder.execute! } + shared_examples 'Packages::Maven::PackageFinder examples' do + describe '#execute!' do + subject { finder.execute! } - shared_examples 'handling valid and invalid paths' do - context 'with a valid path' do - let(:param_path) { package.maven_metadatum.path } + shared_examples 'handling valid and invalid paths' do + context 'with a valid path' do + let(:param_path) { package.maven_metadatum.path } - it { is_expected.to eq(package) } + it { is_expected.to eq(package) } + end + + context 'with an invalid path' do + let(:param_path) { 'com/example/my-app/1.0-SNAPSHOT' } + + it 'raises an error' do + expect { subject }.to raise_error(ActiveRecord::RecordNotFound) + end + end end - context 'with an invalid path' do - let(:param_path) { 'com/example/my-app/1.0-SNAPSHOT' } + context 'within the project' do + let(:param_project) { 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 + + it_behaves_like 'handling valid and invalid paths' + end + end + + context 'across all projects' do it 'raises an error' do expect { subject }.to raise_error(ActiveRecord::RecordNotFound) end end - end - context 'within the project' do - let(:param_project) { project } + context 'versionless maven-metadata.xml package' do + let_it_be(:sub_group1) { create(:group, parent: group) } + let_it_be(:sub_group2) { create(:group, parent: group) } + let_it_be(:project1) { create(:project, group: sub_group1) } + let_it_be(:project2) { create(:project, group: sub_group2) } + let_it_be(:project3) { create(:project, group: sub_group1) } + let_it_be(:package_name) { 'foo' } + let_it_be(:package1) { create(:maven_package, project: project1, name: package_name, version: nil) } + 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(:param_path) { package_name } + + before do + sub_group1.add_developer(user) + sub_group2.add_developer(user) + # the package with the most recently published file should be returned + create(:package_file, :xml, package: package2) + end - it_behaves_like 'handling valid and invalid paths' - 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 'within a group' do - let(:param_group) { group } + context 'without order by package file' do + it { is_expected.to eq(package3) } + end - it_behaves_like 'handling valid and invalid paths' - end + context 'with order by package file' do + let(:param_order_by_package_file) { true } + + it { is_expected.to eq(package2) } + end + 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 'across all projects' do - it 'raises an error' do - expect { subject }.to raise_error(ActiveRecord::RecordNotFound) + 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 + end end end + end + + context 'when the maven_metadata_by_path_with_optimization_fence feature flag is off' do + before do + stub_feature_flags(maven_metadata_by_path_with_optimization_fence: false) + end - context 'versionless maven-metadata.xml package' do - let_it_be(:sub_group1) { create(:group, parent: group) } - let_it_be(:sub_group2) { create(:group, parent: group) } - let_it_be(:project1) { create(:project, group: sub_group1) } - let_it_be(:project2) { create(:project, group: sub_group2) } - let_it_be(:project3) { create(:project, group: sub_group1) } - let_it_be(:package_name) { 'foo' } - let_it_be(:package1) { create(:maven_package, project: project1, name: package_name, version: nil) } - 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(:param_path) { package_name } - - before do - sub_group1.add_developer(user) - sub_group2.add_developer(user) - # the package with the most recently published file should be returned - create(:package_file, :xml, package: package2) - end + it_behaves_like 'Packages::Maven::PackageFinder examples' + end + + context 'when the maven_metadata_by_path_with_optimization_fence feature flag is on' do + before do + stub_feature_flags(maven_metadata_by_path_with_optimization_fence: true) + end + + 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 - it { is_expected.to eq(package2) } + expect(sql).to include('WITH "maven_metadata_by_path" AS') end end end diff --git a/spec/finders/pending_todos_finder_spec.rb b/spec/finders/pending_todos_finder_spec.rb index 10d3c2905be..b17915f0d59 100644 --- a/spec/finders/pending_todos_finder_spec.rb +++ b/spec/finders/pending_todos_finder_spec.rb @@ -4,13 +4,15 @@ require 'spec_helper' RSpec.describe PendingTodosFinder do let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:users) { [user, user2] } describe '#execute' do it 'returns only pending todos' do create(:todo, :done, user: user) todo = create(:todo, :pending, user: user) - todos = described_class.new(user).execute + todos = described_class.new(users).execute expect(todos).to eq([todo]) end @@ -22,7 +24,7 @@ RSpec.describe PendingTodosFinder do create(:todo, :pending, user: user, project: project2) todo = create(:todo, :pending, user: user, project: project1) - todos = described_class.new(user, project_id: project1.id).execute + todos = described_class.new(users, project_id: project1.id).execute expect(todos).to eq([todo]) end @@ -34,7 +36,7 @@ RSpec.describe PendingTodosFinder do create(:todo, :pending, user: user, target: note) - todos = described_class.new(user, target_id: issue.id).execute + todos = described_class.new(users, target_id: issue.id).execute expect(todos).to eq([todo]) end @@ -46,7 +48,7 @@ RSpec.describe PendingTodosFinder do create(:todo, :pending, user: user, target: note) - todos = described_class.new(user, target_type: issue.class.name).execute + todos = described_class.new(users, target_type: issue.class.name).execute expect(todos).to eq([todo]) end @@ -55,7 +57,7 @@ RSpec.describe PendingTodosFinder do create(:todo, :pending, user: user, commit_id: '456') todo = create(:todo, :pending, user: user, commit_id: '123') - todos = described_class.new(user, commit_id: '123').execute + todos = described_class.new(users, commit_id: '123').execute expect(todos).to eq([todo]) end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 4d9ff30daba..a178261e899 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -129,6 +129,12 @@ RSpec.describe ProjectsFinder do it { is_expected.to eq([public_project]) } end + + context 'as string' do + let(:params) { { visibility_level: Gitlab::VisibilityLevel::INTERNAL.to_s } } + + it { is_expected.to eq([internal_project]) } + end end describe 'filter by tags' do diff --git a/spec/finders/repositories/branch_names_finder_spec.rb b/spec/finders/repositories/branch_names_finder_spec.rb new file mode 100644 index 00000000000..4d8bfcc0f20 --- /dev/null +++ b/spec/finders/repositories/branch_names_finder_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +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' + ) + end + end +end diff --git a/spec/finders/repositories/previous_tag_finder_spec.rb b/spec/finders/repositories/changelog_tag_finder_spec.rb index b332dd158d1..cd79beb3e9e 100644 --- a/spec/finders/repositories/previous_tag_finder_spec.rb +++ b/spec/finders/repositories/changelog_tag_finder_spec.rb @@ -2,11 +2,18 @@ require 'spec_helper' -RSpec.describe Repositories::PreviousTagFinder do +RSpec.describe Repositories::ChangelogTagFinder do let(:project) { build_stubbed(:project) } let(:finder) { described_class.new(project) } describe '#execute' do + context 'when the regular expression is invalid' do + it 'raises Gitlab::Changelog::Error' do + expect { described_class.new(project, regex: 'foo+*').execute('1.2.3') } + .to raise_error(Gitlab::Changelog::Error) + end + end + context 'when there is a previous tag' do it 'returns the previous tag' do tag1 = double(:tag1, name: 'v1.0.0') @@ -15,10 +22,11 @@ RSpec.describe Repositories::PreviousTagFinder do tag4 = double(:tag4, name: '0.9.0') tag5 = double(:tag5, name: 'v0.8.0-pre1') tag6 = double(:tag6, name: 'v0.7.0') + tag7 = double(:tag7, name: '0.5.0+42.ee.0') allow(project.repository) .to receive(:tags) - .and_return([tag1, tag3, tag2, tag4, tag5, tag6]) + .and_return([tag1, tag3, tag2, tag4, tag5, tag6, tag7]) expect(finder.execute('2.1.0')).to eq(tag3) expect(finder.execute('2.0.0')).to eq(tag2) @@ -26,6 +34,7 @@ RSpec.describe Repositories::PreviousTagFinder do expect(finder.execute('1.0.1')).to eq(tag1) expect(finder.execute('1.0.0')).to eq(tag4) expect(finder.execute('0.9.0')).to eq(tag6) + expect(finder.execute('0.6.0')).to eq(tag7) end end diff --git a/spec/finders/user_group_notification_settings_finder_spec.rb b/spec/finders/user_group_notification_settings_finder_spec.rb index 453da691866..b9d800d8e55 100644 --- a/spec/finders/user_group_notification_settings_finder_spec.rb +++ b/spec/finders/user_group_notification_settings_finder_spec.rb @@ -129,4 +129,37 @@ RSpec.describe UserGroupNotificationSettingsFinder do end end end + + context 'preloading `emails_disabled`' do + let_it_be(:root_group) { create(:group) } + let_it_be(:sub_group) { create(:group, parent: root_group) } + let_it_be(:sub_sub_group) { create(:group, parent: sub_group) } + + let_it_be(:another_root_group) { create(:group) } + let_it_be(:sub_group_with_emails_disabled) { create(:group, emails_disabled: true, parent: another_root_group) } + let_it_be(:another_sub_sub_group) { create(:group, parent: sub_group_with_emails_disabled) } + + let_it_be(:root_group_with_emails_disabled) { create(:group, emails_disabled: true) } + let_it_be(:group) { create(:group, parent: root_group_with_emails_disabled) } + + let(:groups) { Group.where(id: [sub_sub_group, another_sub_sub_group, group]) } + + before do + described_class.new(user, groups).execute + end + + it 'preloads the `group.emails_disabled` method' do + recorder = ActiveRecord::QueryRecorder.new do + groups.each(&:emails_disabled?) + end + + expect(recorder.count).to eq(0) + end + + it 'preloads the `group.emails_disabled` method correctly' do + groups.each do |group| + expect(group.emails_disabled?).to eq(Group.find(group.id).emails_disabled?) # compare the memoized and the freshly loaded value + end + end + end end |