diff options
Diffstat (limited to 'spec/finders')
-rw-r--r-- | spec/finders/bulk_imports/entities_finder_spec.rb | 32 | ||||
-rw-r--r-- | spec/finders/bulk_imports/imports_finder_spec.rb | 24 | ||||
-rw-r--r-- | spec/finders/ci/jobs_finder_spec.rb | 22 | ||||
-rw-r--r-- | spec/finders/concerns/finder_methods_spec.rb | 51 | ||||
-rw-r--r-- | spec/finders/concerns/finder_with_cross_project_access_spec.rb | 4 | ||||
-rw-r--r-- | spec/finders/keys_finder_spec.rb | 55 | ||||
-rw-r--r-- | spec/finders/packages/build_infos_for_many_packages_finder_spec.rb | 136 | ||||
-rw-r--r-- | spec/finders/packages/group_packages_finder_spec.rb | 16 | ||||
-rw-r--r-- | spec/finders/packages/packages_finder_spec.rb | 16 | ||||
-rw-r--r-- | spec/finders/releases/group_releases_finder_spec.rb | 15 | ||||
-rw-r--r-- | spec/finders/user_recent_events_finder_spec.rb | 356 | ||||
-rw-r--r-- | spec/finders/users_finder_spec.rb | 50 |
12 files changed, 537 insertions, 240 deletions
diff --git a/spec/finders/bulk_imports/entities_finder_spec.rb b/spec/finders/bulk_imports/entities_finder_spec.rb index e053011b60d..54c792cb4d8 100644 --- a/spec/finders/bulk_imports/entities_finder_spec.rb +++ b/spec/finders/bulk_imports/entities_finder_spec.rb @@ -51,7 +51,7 @@ RSpec.describe BulkImports::EntitiesFinder do end context 'when status is specified' do - subject { described_class.new(user: user, status: 'failed') } + subject { described_class.new(user: user, params: { status: 'failed' }) } it 'returns a list of import entities filtered by status' do expect(subject.execute) @@ -61,7 +61,7 @@ RSpec.describe BulkImports::EntitiesFinder do end context 'when invalid status is specified' do - subject { described_class.new(user: user, status: 'invalid') } + subject { described_class.new(user: user, params: { status: 'invalid' }) } it 'does not filter entities by status' do expect(subject.execute) @@ -74,11 +74,37 @@ RSpec.describe BulkImports::EntitiesFinder do end context 'when bulk import and status are specified' do - subject { described_class.new(user: user, bulk_import: user_import_2, status: 'finished') } + subject { described_class.new(user: user, bulk_import: user_import_2, params: { status: 'finished' }) } it 'returns matched import entities' do expect(subject.execute).to contain_exactly(finished_entity_2) end end + + context 'when order is specifed' do + subject { described_class.new(user: user, params: { sort: order }) } + + context 'when order is specified as asc' do + let(:order) { :asc } + + it 'returns entities sorted ascending' do + expect(subject.execute).to eq([ + started_entity_1, finished_entity_1, failed_entity_1, + started_entity_2, finished_entity_2, failed_entity_2 + ]) + end + end + + context 'when order is specified as desc' do + let(:order) { :desc } + + it 'returns entities sorted descending' do + expect(subject.execute).to eq([ + failed_entity_2, finished_entity_2, started_entity_2, + failed_entity_1, finished_entity_1, started_entity_1 + ]) + end + end + end end end diff --git a/spec/finders/bulk_imports/imports_finder_spec.rb b/spec/finders/bulk_imports/imports_finder_spec.rb index aac83c86c84..2f550514a33 100644 --- a/spec/finders/bulk_imports/imports_finder_spec.rb +++ b/spec/finders/bulk_imports/imports_finder_spec.rb @@ -16,19 +16,39 @@ RSpec.describe BulkImports::ImportsFinder do end context 'when status is specified' do - subject { described_class.new(user: user, status: 'started') } + subject { described_class.new(user: user, params: { status: 'started' }) } it 'returns a list of import entities filtered by status' do expect(subject.execute).to contain_exactly(started_import) end context 'when invalid status is specified' do - subject { described_class.new(user: user, status: 'invalid') } + subject { described_class.new(user: user, params: { status: 'invalid' }) } it 'does not filter entities by status' do expect(subject.execute).to contain_exactly(started_import, finished_import) end end end + + context 'when order is specifed' do + subject { described_class.new(user: user, params: { sort: order }) } + + context 'when order is specified as asc' do + let(:order) { :asc } + + it 'returns entities sorted ascending' do + expect(subject.execute).to eq([started_import, finished_import]) + end + end + + context 'when order is specified as desc' do + let(:order) { :desc } + + it 'returns entities sorted descending' do + expect(subject.execute).to eq([finished_import, started_import]) + end + end + end end end diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb index 959716b1fd3..45e8cf5a582 100644 --- a/spec/finders/ci/jobs_finder_spec.rb +++ b/spec/finders/ci/jobs_finder_spec.rb @@ -7,9 +7,9 @@ RSpec.describe Ci::JobsFinder, '#execute' do let_it_be(:admin) { create(:user, :admin) } let_it_be(:project) { create(:project, :private, public_builds: false) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } - let_it_be(:job_1) { create(:ci_build) } - let_it_be(:job_2) { create(:ci_build, :running) } - let_it_be(:job_3) { create(:ci_build, :success, pipeline: pipeline, name: 'build') } + let_it_be(:pending_job) { create(:ci_build, :pending) } + let_it_be(:running_job) { create(:ci_build, :running) } + let_it_be(:successful_job) { create(:ci_build, :success, pipeline: pipeline, name: 'build') } let(:params) { {} } @@ -17,7 +17,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do subject { described_class.new(current_user: admin, params: params).execute } it 'returns all jobs' do - expect(subject).to match_array([job_1, job_2, job_3]) + expect(subject).to match_array([pending_job, running_job, successful_job]) end context 'non admin user' do @@ -37,7 +37,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do end context 'scope is present' do - let(:jobs) { [job_1, job_2, job_3] } + let(:jobs) { [pending_job, running_job, successful_job] } where(:scope, :index) do [ @@ -55,11 +55,11 @@ RSpec.describe Ci::JobsFinder, '#execute' do end context 'scope is an array' do - let(:jobs) { [job_1, job_2, job_3] } - let(:params) {{ scope: ['running'] }} + let(:jobs) { [pending_job, running_job, successful_job, canceled_job] } + let(:params) {{ scope: %w'running success' }} it 'filters by the job statuses in the scope' do - expect(subject).to match_array([job_2]) + expect(subject).to contain_exactly(running_job, successful_job) end end end @@ -73,7 +73,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do end it 'returns jobs for the specified project' do - expect(subject).to match_array([job_3]) + expect(subject).to match_array([successful_job]) end end @@ -99,7 +99,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do context 'when pipeline is present' do before_all do project.add_maintainer(user) - job_3.update!(retried: true) + successful_job.update!(retried: true) end let_it_be(:job_4) { create(:ci_build, :success, pipeline: pipeline, name: 'build') } @@ -122,7 +122,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do let(:params) { { include_retried: true } } it 'returns retried jobs' do - expect(subject).to match_array([job_3, job_4]) + expect(subject).to match_array([successful_job, job_4]) end end end diff --git a/spec/finders/concerns/finder_methods_spec.rb b/spec/finders/concerns/finder_methods_spec.rb index 195449d70c3..09ec8110129 100644 --- a/spec/finders/concerns/finder_methods_spec.rb +++ b/spec/finders/concerns/finder_methods_spec.rb @@ -12,7 +12,7 @@ RSpec.describe FinderMethods do end def execute - Project.all.order(id: :desc) + Project.where.not(name: 'foo').order(id: :desc) end private @@ -21,22 +21,30 @@ RSpec.describe FinderMethods do end end - let(:user) { create(:user) } - let(:finder) { finder_class.new(user) } - let(:authorized_project) { create(:project) } - let(:unauthorized_project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:authorized_project) { create(:project) } + let_it_be(:unmatched_project) { create(:project, name: 'foo') } + let_it_be(:unauthorized_project) { create(:project) } - before do + subject(:finder) { finder_class.new(user) } + + before_all do authorized_project.add_developer(user) + unmatched_project.add_developer(user) end + # rubocop:disable Rails/FindById describe '#find_by!' do it 'returns the project if the user has access' do expect(finder.find_by!(id: authorized_project.id)).to eq(authorized_project) end - it 'raises not found when the project is not found' do - expect { finder.find_by!(id: 0) }.to raise_error(ActiveRecord::RecordNotFound) + it 'raises not found when the project is not found by id' do + expect { finder.find_by!(id: non_existing_record_id) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises not found when the project is not found by filter' do + expect { finder.find_by!(id: unmatched_project.id) }.to raise_error(ActiveRecord::RecordNotFound) end it 'raises not found the user does not have access' do @@ -53,19 +61,34 @@ RSpec.describe FinderMethods do finder.find_by!(id: authorized_project.id) end end + # rubocop:enable Rails/FindById describe '#find' do it 'returns the project if the user has access' do expect(finder.find(authorized_project.id)).to eq(authorized_project) end - it 'raises not found when the project is not found' do - expect { finder.find(0) }.to raise_error(ActiveRecord::RecordNotFound) + it 'raises not found when the project is not found by id' do + expect { finder.find(non_existing_record_id) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises not found when the project is not found by filter' do + expect { finder.find(unmatched_project.id) }.to raise_error(ActiveRecord::RecordNotFound) end it 'raises not found the user does not have access' do expect { finder.find(unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound) end + + it 'ignores ordering' do + # Memoise the finder result so we can add message expectations to it + relation = finder.execute + allow(finder).to receive(:execute).and_return(relation) + + expect(relation).to receive(:reorder).with(nil).and_call_original + + finder.find(authorized_project.id) + end end describe '#find_by' do @@ -73,8 +96,12 @@ RSpec.describe FinderMethods do expect(finder.find_by(id: authorized_project.id)).to eq(authorized_project) end - it 'returns nil when the project is not found' do - expect(finder.find_by(id: 0)).to be_nil + it 'returns nil when the project is not found by id' do + expect(finder.find_by(id: non_existing_record_id)).to be_nil + end + + it 'returns nil when the project is not found by filter' do + expect(finder.find_by(id: unmatched_project.id)).to be_nil end it 'returns nil when the user does not have access' do diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb index 116b523bd99..0798528c200 100644 --- a/spec/finders/concerns/finder_with_cross_project_access_spec.rb +++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb @@ -93,11 +93,11 @@ RSpec.describe FinderWithCrossProjectAccess do it 'checks the accessibility of the subject directly' do expect_access_check_on_result - finder.find_by!(id: result.id) + finder.find(result.id) end it 're-enables the check after the find failed' do - finder.find_by!(id: non_existing_record_id) rescue ActiveRecord::RecordNotFound + finder.find(non_existing_record_id) rescue ActiveRecord::RecordNotFound expect(finder.instance_variable_get(:@should_skip_cross_project_check)) .to eq(false) diff --git a/spec/finders/keys_finder_spec.rb b/spec/finders/keys_finder_spec.rb index 277c852c953..332aa7afde1 100644 --- a/spec/finders/keys_finder_spec.rb +++ b/spec/finders/keys_finder_spec.rb @@ -5,23 +5,22 @@ require 'spec_helper' RSpec.describe KeysFinder do subject { described_class.new(params).execute } - let(:user) { create(:user) } - let(:params) { {} } - - let!(:key_1) do - create(:personal_key, + let_it_be(:user) { create(:user) } + let_it_be(:key_1) do + create(:rsa_key_4096, last_used_at: 7.days.ago, user: user, - key: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=', - fingerprint: 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1', - fingerprint_sha256: 'nUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo/lCg') + fingerprint: 'df:73:db:29:3c:a5:32:cf:09:17:7e:8e:9d:de:d7:f7', + fingerprint_sha256: 'ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g') end - let!(:key_2) { create(:personal_key, last_used_at: nil, user: user) } - let!(:key_3) { create(:personal_key, last_used_at: 2.days.ago) } + let_it_be(:key_2) { create(:personal_key_4096, last_used_at: nil, user: user) } + let_it_be(:key_3) { create(:personal_key_4096, last_used_at: 2.days.ago) } + + let(:params) { {} } context 'key_type' do - let!(:deploy_key) { create(:deploy_key) } + let_it_be(:deploy_key) { create(:deploy_key) } context 'when `key_type` is `ssh`' do before do @@ -64,35 +63,41 @@ RSpec.describe KeysFinder do end context 'with valid fingerprints' do - let!(:deploy_key) do - create(:deploy_key, - user: user, - key: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1017k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=', - fingerprint: '8a:4a:12:92:0b:50:47:02:d4:5a:8e:a9:44:4e:08:b4', - fingerprint_sha256: '4DPHOVNh53i9dHb5PpY2vjfyf5qniTx1/pBFPoZLDdk') - end + let_it_be(:deploy_key) { create(:rsa_deploy_key_5120, user: user) } context 'personal key with valid MD5 params' do context 'with an existent fingerprint' do before do - params[:fingerprint] = 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1' + params[:fingerprint] = 'df:73:db:29:3c:a5:32:cf:09:17:7e:8e:9d:de:d7:f7' end it 'returns the key' do expect(subject).to eq(key_1) expect(subject.user).to eq(user) end + + context 'with FIPS mode', :fips_mode do + it 'raises InvalidFingerprint' do + expect { subject }.to raise_error(KeysFinder::InvalidFingerprint) + end + end end context 'deploy key with an existent fingerprint' do before do - params[:fingerprint] = '8a:4a:12:92:0b:50:47:02:d4:5a:8e:a9:44:4e:08:b4' + params[:fingerprint] = 'fe:fa:3a:4d:7d:51:ec:bf:c7:64:0c:96:d0:17:8a:d0' end it 'returns the key' do expect(subject).to eq(deploy_key) expect(subject.user).to eq(user) end + + context 'with FIPS mode', :fips_mode do + it 'raises InvalidFingerprint' do + expect { subject }.to raise_error(KeysFinder::InvalidFingerprint) + end + end end context 'with a non-existent fingerprint' do @@ -103,13 +108,19 @@ RSpec.describe KeysFinder do it 'returns nil' do expect(subject).to be_nil end + + context 'with FIPS mode', :fips_mode do + it 'raises InvalidFingerprint' do + expect { subject }.to raise_error(KeysFinder::InvalidFingerprint) + end + end end end context 'personal key with valid SHA256 params' do context 'with an existent fingerprint' do before do - params[:fingerprint] = 'SHA256:nUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo/lCg' + params[:fingerprint] = 'SHA256:ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g' end it 'returns key' do @@ -120,7 +131,7 @@ RSpec.describe KeysFinder do context 'deploy key with an existent fingerprint' do before do - params[:fingerprint] = 'SHA256:4DPHOVNh53i9dHb5PpY2vjfyf5qniTx1/pBFPoZLDdk' + params[:fingerprint] = 'SHA256:PCCupLbFHScm4AbEufbGDvhBU27IM0MVAor715qKQK8' end it 'returns key' do diff --git a/spec/finders/packages/build_infos_for_many_packages_finder_spec.rb b/spec/finders/packages/build_infos_for_many_packages_finder_spec.rb new file mode 100644 index 00000000000..f3c79d0c825 --- /dev/null +++ b/spec/finders/packages/build_infos_for_many_packages_finder_spec.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::BuildInfosForManyPackagesFinder do + using RSpec::Parameterized::TableSyntax + + let_it_be(:package) { create(:package) } + let_it_be(:build_infos) { create_list(:package_build_info, 5, :with_pipeline, package: package) } + let_it_be(:build_info_with_empty_pipeline) { create(:package_build_info, package: package) } + + let_it_be(:other_package) { create(:package) } + let_it_be(:other_build_infos) { create_list(:package_build_info, 5, :with_pipeline, package: other_package) } + let_it_be(:other_build_info_with_empty_pipeline) { create(:package_build_info, package: other_package) } + + let_it_be(:all_build_infos) { build_infos + other_build_infos } + + let(:finder) { described_class.new(packages, params) } + let(:packages) { nil } + let(:first) { nil } + let(:last) { nil } + let(:after) { nil } + let(:before) { nil } + let(:max_page_size) { nil } + let(:support_next_page) { false } + let(:params) do + { + first: first, + last: last, + after: after, + before: before, + max_page_size: max_page_size, + support_next_page: support_next_page + } + end + + describe '#execute' do + subject { finder.execute } + + shared_examples 'returning the expected build infos' do + let(:expected_build_infos) do + expected_build_infos_indexes.map do |idx| + all_build_infos[idx] + end + end + + let(:after) do + all_build_infos[after_index].pipeline_id if after_index + end + + let(:before) do + all_build_infos[before_index].pipeline_id if before_index + end + + it { is_expected.to eq(expected_build_infos) } + end + + context 'with nil packages' do + let(:packages) { nil } + + it { is_expected.to be_empty } + end + + context 'with [] packages' do + let(:packages) { [] } + + it { is_expected.to be_empty } + end + + context 'with empy scope packages' do + let(:packages) { Packages::Package.none } + + it { is_expected.to be_empty } + end + + context 'with a single package' do + let(:packages) { package.id } + + # rubocop: disable Layout/LineLength + where(:first, :last, :after_index, :before_index, :max_page_size, :support_next_page, :expected_build_infos_indexes) do + # F L AI BI MPS SNP + nil | nil | nil | nil | nil | false | [4, 3, 2, 1, 0] + nil | nil | nil | nil | 10 | false | [4, 3, 2, 1, 0] + nil | nil | nil | nil | 2 | false | [4, 3] + 2 | nil | nil | nil | nil | false | [4, 3] + 2 | nil | nil | nil | nil | true | [4, 3, 2] + 2 | nil | 3 | nil | nil | false | [2, 1] + 2 | nil | 3 | nil | nil | true | [2, 1, 0] + 3 | nil | 4 | nil | 2 | false | [3, 2] + 3 | nil | 4 | nil | 2 | true | [3, 2, 1] + nil | 2 | nil | nil | nil | false | [1, 0] + nil | 2 | nil | nil | nil | true | [2, 1, 0] + nil | 2 | nil | 1 | nil | false | [3, 2] + nil | 2 | nil | 1 | nil | true | [4, 3, 2] + nil | 3 | nil | 0 | 2 | false | [2, 1] + nil | 3 | nil | 0 | 2 | true | [3, 2, 1] + end + # rubocop: enable Layout/LineLength + + with_them do + it_behaves_like 'returning the expected build infos' + end + end + + context 'with many packages' do + let(:packages) { [package.id, other_package.id] } + + # using after_index/before_index when receiving multiple packages doesn't + # make sense but we still verify here that the behavior is coherent. + # rubocop: disable Layout/LineLength + where(:first, :last, :after_index, :before_index, :max_page_size, :support_next_page, :expected_build_infos_indexes) do + # F L AI BI MPS SNP + nil | nil | nil | nil | nil | false | [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + nil | nil | nil | nil | 10 | false | [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + nil | nil | nil | nil | 2 | false | [9, 8, 4, 3] + 2 | nil | nil | nil | nil | false | [9, 8, 4, 3] + 2 | nil | nil | nil | nil | true | [9, 8, 7, 4, 3, 2] + 2 | nil | 3 | nil | nil | false | [2, 1] + 2 | nil | 3 | nil | nil | true | [2, 1, 0] + 3 | nil | 4 | nil | 2 | false | [3, 2] + 3 | nil | 4 | nil | 2 | true | [3, 2, 1] + nil | 2 | nil | nil | nil | false | [6, 5, 1, 0] + nil | 2 | nil | nil | nil | true | [7, 6, 5, 2, 1, 0] + nil | 2 | nil | 1 | nil | false | [6, 5, 3, 2] + nil | 2 | nil | 1 | nil | true | [7, 6, 5, 4, 3, 2] + nil | 3 | nil | 0 | 2 | false | [6, 5, 2, 1] + nil | 3 | nil | 0 | 2 | true | [7, 6, 5, 3, 2, 1] + end + + with_them do + it_behaves_like 'returning the expected build infos' + end + # rubocop: enable Layout/LineLength + end + end +end diff --git a/spec/finders/packages/group_packages_finder_spec.rb b/spec/finders/packages/group_packages_finder_spec.rb index c2dbfb59eb2..954db6481cd 100644 --- a/spec/finders/packages/group_packages_finder_spec.rb +++ b/spec/finders/packages/group_packages_finder_spec.rb @@ -149,6 +149,22 @@ RSpec.describe Packages::GroupPackagesFinder do it { is_expected.to match_array([package1, package2]) } end + context 'preload_pipelines' do + it 'preloads pipelines by default' do + expect(Packages::Package).to receive(:preload_pipelines).and_call_original + expect(subject).to match_array([package1, package2]) + end + + context 'set to false' do + let(:params) { { preload_pipelines: false } } + + it 'does not preload pipelines' do + expect(Packages::Package).not_to receive(:preload_pipelines) + expect(subject).to match_array([package1, package2]) + end + end + end + context 'with package_name' do let_it_be(:named_package) { create(:maven_package, project: project, name: 'maven') } diff --git a/spec/finders/packages/packages_finder_spec.rb b/spec/finders/packages/packages_finder_spec.rb index b72f4aab3ec..6cea0a44541 100644 --- a/spec/finders/packages/packages_finder_spec.rb +++ b/spec/finders/packages/packages_finder_spec.rb @@ -81,6 +81,22 @@ RSpec.describe ::Packages::PackagesFinder do it { is_expected.to match_array([conan_package, maven_package]) } end + context 'preload_pipelines' do + it 'preloads pipelines by default' do + expect(Packages::Package).to receive(:preload_pipelines).and_call_original + expect(subject).to match_array([maven_package, conan_package]) + end + + context 'set to false' do + let(:params) { { preload_pipelines: false } } + + it 'does not preload pipelines' do + expect(Packages::Package).not_to receive(:preload_pipelines) + expect(subject).to match_array([maven_package, conan_package]) + end + end + end + it_behaves_like 'concerning versionless param' it_behaves_like 'concerning package statuses' end diff --git a/spec/finders/releases/group_releases_finder_spec.rb b/spec/finders/releases/group_releases_finder_spec.rb index b8899a8ee40..5eac6f4fbdc 100644 --- a/spec/finders/releases/group_releases_finder_spec.rb +++ b/spec/finders/releases/group_releases_finder_spec.rb @@ -95,8 +95,6 @@ RSpec.describe Releases::GroupReleasesFinder do end describe 'with subgroups' do - let(:params) { { include_subgroups: true } } - subject(:releases) { described_class.new(group, user, params).execute(**args) } context 'with a single-level subgroup' do @@ -164,22 +162,12 @@ RSpec.describe Releases::GroupReleasesFinder do end end - context 'when the user a guest on the group' do - before do - group.add_guest(user) - end - - it 'returns all releases' do - expect(releases).to match_array([v1_1_1, v1_1_0, v6, v1_0_0, p3]) - end - end - context 'performance testing' do shared_examples 'avoids N+1 queries' do |query_params = {}| context 'with subgroups' do let(:params) { query_params } - it 'include_subgroups avoids N+1 queries' do + it 'subgroups avoids N+1 queries' do control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do releases end.count @@ -196,7 +184,6 @@ RSpec.describe Releases::GroupReleasesFinder do end it_behaves_like 'avoids N+1 queries' - it_behaves_like 'avoids N+1 queries', { simple: true } end end end diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb index 6019d22059d..d7f7bb9cebe 100644 --- a/spec/finders/user_recent_events_finder_spec.rb +++ b/spec/finders/user_recent_events_finder_spec.rb @@ -8,9 +8,9 @@ RSpec.describe UserRecentEventsFinder do let_it_be(:private_project) { create(:project, :private, creator: project_owner) } let_it_be(:internal_project) { create(:project, :internal, creator: project_owner) } let_it_be(:public_project) { create(:project, :public, creator: project_owner) } - let!(:private_event) { create(:event, project: private_project, author: project_owner) } - let!(:internal_event) { create(:event, project: internal_project, author: project_owner) } - let!(:public_event) { create(:event, project: public_project, author: project_owner) } + let_it_be(:private_event) { create(:event, project: private_project, author: project_owner) } + let_it_be(:internal_event) { create(:event, project: internal_project, author: project_owner) } + let_it_be(:public_event) { create(:event, project: public_project, author: project_owner) } let_it_be(:issue) { create(:issue, project: public_project) } let(:limit) { nil } @@ -18,210 +18,266 @@ RSpec.describe UserRecentEventsFinder do subject(:finder) { described_class.new(current_user, project_owner, nil, params) } - describe '#execute' do - context 'when profile is public' do - it 'returns all the events' do - expect(finder.execute).to include(private_event, internal_event, public_event) + shared_examples 'UserRecentEventsFinder examples' do + describe '#execute' do + context 'when profile is public' do + it 'returns all the events' do + expect(finder.execute).to include(private_event, internal_event, public_event) + end end - end - context 'when profile is private' do - it 'returns no event' do - allow(Ability).to receive(:allowed?).and_call_original - allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false) + context 'when profile is private' do + it 'returns no event' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false) - expect(finder.execute).to be_empty + expect(finder.execute).to be_empty + end end - end - it 'does not include the events if the user cannot read cross project' do - allow(Ability).to receive(:allowed?).and_call_original - expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false } + it 'does not include the events if the user cannot read cross project' do + allow(Ability).to receive(:allowed?).and_call_original + expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false } - expect(finder.execute).to be_empty - end + expect(finder.execute).to be_empty + end - context 'events from multiple users' do - let_it_be(:second_user, reload: true) { create(:user) } - let_it_be(:private_project_second_user) { create(:project, :private, creator: second_user) } + context 'events from multiple users' do + let_it_be(:second_user, reload: true) { create(:user) } + let_it_be(:private_project_second_user) { create(:project, :private, creator: second_user) } - let(:internal_project_second_user) { create(:project, :internal, creator: second_user) } - let(:public_project_second_user) { create(:project, :public, creator: second_user) } - let!(:private_event_second_user) { create(:event, project: private_project_second_user, author: second_user) } - let!(:internal_event_second_user) { create(:event, project: internal_project_second_user, author: second_user) } - let!(:public_event_second_user) { create(:event, project: public_project_second_user, author: second_user) } + let_it_be(:internal_project_second_user) { create(:project, :internal, creator: second_user) } + let_it_be(:public_project_second_user) { create(:project, :public, creator: second_user) } + let_it_be(:private_event_second_user) { create(:event, project: private_project_second_user, author: second_user) } + let_it_be(:internal_event_second_user) { create(:event, project: internal_project_second_user, author: second_user) } + let_it_be(:public_event_second_user) { create(:event, project: public_project_second_user, author: second_user) } - it 'includes events from all users', :aggregate_failures do - events = described_class.new(current_user, [project_owner, second_user], nil, params).execute + it 'includes events from all users', :aggregate_failures do + events = described_class.new(current_user, [project_owner, second_user], nil, params).execute - expect(events).to include(private_event, internal_event, public_event) - expect(events).to include(private_event_second_user, internal_event_second_user, public_event_second_user) - expect(events.size).to eq(6) - end + expect(events).to include(private_event, internal_event, public_event) + expect(events).to include(private_event_second_user, internal_event_second_user, public_event_second_user) + expect(events.size).to eq(6) + end - context 'selected events' do - let!(:push_event) { create(:push_event, project: public_project, author: project_owner) } - let!(:push_event_second_user) { create(:push_event, project: public_project_second_user, author: second_user) } + context 'selected events' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:push_event1) { create(:push_event, project: public_project, author: project_owner) } + let_it_be(:push_event2) { create(:push_event, project: public_project_second_user, author: second_user) } + let_it_be(:merge_event1) { create(:event, :merged, target_type: MergeRequest.to_s, project: public_project, author: project_owner) } + let_it_be(:merge_event2) { create(:event, :merged, target_type: MergeRequest.to_s, project: public_project_second_user, author: second_user) } + let_it_be(:comment_event1) { create(:event, :commented, target_type: Note.to_s, project: public_project, author: project_owner) } + let_it_be(:comment_event2) { create(:event, :commented, target_type: DiffNote.to_s, project: public_project, author: project_owner) } + let_it_be(:comment_event3) { create(:event, :commented, target_type: DiscussionNote.to_s, project: public_project_second_user, author: second_user) } + let_it_be(:issue_event1) { create(:event, :created, project: public_project, target: issue, author: project_owner) } + let_it_be(:issue_event2) { create(:event, :updated, project: public_project, target: issue, author: project_owner) } + let_it_be(:issue_event3) { create(:event, :closed, project: public_project_second_user, target: issue, author: second_user) } + let_it_be(:wiki_event1) { create(:wiki_page_event, project: public_project, author: project_owner) } + let_it_be(:wiki_event2) { create(:wiki_page_event, project: public_project_second_user, author: second_user) } + let_it_be(:design_event1) { create(:design_event, project: public_project, author: project_owner) } + let_it_be(:design_event2) { create(:design_updated_event, project: public_project_second_user, author: second_user) } + + where(:event_filter, :ordered_expected_events) do + EventFilter.new(EventFilter::PUSH) | lazy { [push_event1, push_event2] } + EventFilter.new(EventFilter::MERGED) | lazy { [merge_event1, merge_event2] } + EventFilter.new(EventFilter::COMMENTS) | lazy { [comment_event1, comment_event2, comment_event3] } + EventFilter.new(EventFilter::TEAM) | lazy { [private_event, internal_event, public_event, private_event_second_user, internal_event_second_user, public_event_second_user] } + EventFilter.new(EventFilter::ISSUE) | lazy { [issue_event1, issue_event2, issue_event3] } + EventFilter.new(EventFilter::WIKI) | lazy { [wiki_event1, wiki_event2] } + EventFilter.new(EventFilter::DESIGNS) | lazy { [design_event1, design_event2] } + end - it 'only includes selected events (PUSH) from all users', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::PUSH) - events = described_class.new(current_user, [project_owner, second_user], event_filter, params).execute + with_them do + it 'only returns selected events from all users (id DESC)' do + events = described_class.new(current_user, [project_owner, second_user], event_filter, params).execute - expect(events).to contain_exactly(push_event, push_event_second_user) + expect(events).to eq(ordered_expected_events.reverse) + end + end end - end - it 'does not include events from users with private profile', :aggregate_failures do - allow(Ability).to receive(:allowed?).and_call_original - allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, second_user).and_return(false) + it 'does not include events from users with private profile', :aggregate_failures do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, second_user).and_return(false) - events = described_class.new(current_user, [project_owner, second_user], nil, params).execute + events = described_class.new(current_user, [project_owner, second_user], nil, params).execute - expect(events).to contain_exactly(private_event, internal_event, public_event) - end + expect(events).to contain_exactly(private_event, internal_event, public_event) + end - context 'with pagination params' do - using RSpec::Parameterized::TableSyntax + context 'with pagination params' do + using RSpec::Parameterized::TableSyntax - where(:limit, :offset, :ordered_expected_events) do - nil | nil | lazy { [public_event_second_user, internal_event_second_user, private_event_second_user, public_event, internal_event, private_event] } - 2 | nil | lazy { [public_event_second_user, internal_event_second_user] } - nil | 4 | lazy { [internal_event, private_event] } - 2 | 2 | lazy { [private_event_second_user, public_event] } - end + where(:limit, :offset, :ordered_expected_events) do + nil | nil | lazy { [public_event_second_user, internal_event_second_user, private_event_second_user, public_event, internal_event, private_event] } + 2 | nil | lazy { [public_event_second_user, internal_event_second_user] } + nil | 4 | lazy { [internal_event, private_event] } + 2 | 2 | lazy { [private_event_second_user, public_event] } + end - with_them do - let(:params) { { limit: limit, offset: offset }.compact } + with_them do + let(:params) { { limit: limit, offset: offset }.compact } - it 'returns paginated events sorted by id (DESC)' do - events = described_class.new(current_user, [project_owner, second_user], nil, params).execute + it 'returns paginated events sorted by id (DESC)' do + events = described_class.new(current_user, [project_owner, second_user], nil, params).execute - expect(events).to eq(ordered_expected_events) + expect(events).to eq(ordered_expected_events) + end end end end - end - context 'filter activity events' do - let!(:push_event) { create(:push_event, project: public_project, author: project_owner) } - let!(:merge_event) { create(:event, :merged, project: public_project, author: project_owner) } - let!(:issue_event) { create(:event, :closed, project: public_project, target: issue, author: project_owner) } - let!(:comment_event) { create(:event, :commented, project: public_project, author: project_owner) } - let!(:wiki_event) { create(:wiki_page_event, project: public_project, author: project_owner) } - let!(:design_event) { create(:design_event, project: public_project, author: project_owner) } - let!(:team_event) { create(:event, :joined, project: public_project, author: project_owner) } - - it 'includes all events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::ALL) - events = described_class.new(current_user, project_owner, event_filter, params).execute - - expect(events).to include(private_event, internal_event, public_event) - expect(events).to include(push_event, merge_event, issue_event, comment_event, wiki_event, design_event, team_event) - expect(events.size).to eq(10) - end + context 'filter activity events' do + let_it_be(:push_event) { create(:push_event, project: public_project, author: project_owner) } + let_it_be(:merge_event) { create(:event, :merged, project: public_project, author: project_owner) } + let_it_be(:issue_event) { create(:event, :closed, project: public_project, target: issue, author: project_owner) } + let_it_be(:comment_event) { create(:event, :commented, project: public_project, author: project_owner) } + let_it_be(:wiki_event) { create(:wiki_page_event, project: public_project, author: project_owner) } + let_it_be(:design_event) { create(:design_event, project: public_project, author: project_owner) } + let_it_be(:team_event) { create(:event, :joined, project: public_project, author: project_owner) } + + it 'includes all events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::ALL) + events = described_class.new(current_user, project_owner, event_filter, params).execute + + expect(events).to include(private_event, internal_event, public_event) + expect(events).to include(push_event, merge_event, issue_event, comment_event, wiki_event, design_event, team_event) + expect(events.size).to eq(10) + end - it 'only includes push events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::PUSH) - events = described_class.new(current_user, project_owner, event_filter, params).execute + context 'when unknown filter is given' do + it 'includes returns all events', :aggregate_failures do + event_filter = EventFilter.new('unknown') + allow(event_filter).to receive(:filter).and_return('unknown') - expect(events).to include(push_event) - expect(events.size).to eq(1) - end + events = described_class.new(current_user, [project_owner], event_filter, params).execute - it 'only includes merge events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::MERGED) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(private_event, internal_event, public_event) + expect(events).to include(push_event, merge_event, issue_event, comment_event, wiki_event, design_event, team_event) + expect(events.size).to eq(10) + end + end - expect(events).to include(merge_event) - expect(events.size).to eq(1) - end + it 'only includes push events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::PUSH) + events = described_class.new(current_user, project_owner, event_filter, params).execute - it 'only includes issue events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::ISSUE) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(push_event) + expect(events.size).to eq(1) + end - expect(events).to include(issue_event) - expect(events.size).to eq(1) - end + it 'only includes merge events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::MERGED) + events = described_class.new(current_user, project_owner, event_filter, params).execute - it 'only includes comments events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::COMMENTS) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(merge_event) + expect(events.size).to eq(1) + end - expect(events).to include(comment_event) - expect(events.size).to eq(1) - end + it 'only includes issue events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::ISSUE) + events = described_class.new(current_user, project_owner, event_filter, params).execute - it 'only includes wiki events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::WIKI) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(issue_event) + expect(events.size).to eq(1) + end - expect(events).to include(wiki_event) - expect(events.size).to eq(1) - end + it 'only includes comments events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::COMMENTS) + events = described_class.new(current_user, project_owner, event_filter, params).execute - it 'only includes design events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::DESIGNS) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(comment_event) + expect(events.size).to eq(1) + end - expect(events).to include(design_event) - expect(events.size).to eq(1) - end + it 'only includes wiki events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::WIKI) + events = described_class.new(current_user, project_owner, event_filter, params).execute - it 'only includes team events', :aggregate_failures do - event_filter = EventFilter.new(EventFilter::TEAM) - events = described_class.new(current_user, project_owner, event_filter, params).execute + expect(events).to include(wiki_event) + expect(events.size).to eq(1) + end - expect(events).to include(private_event, internal_event, public_event, team_event) - expect(events.size).to eq(4) - end - end + it 'only includes design events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::DESIGNS) + events = described_class.new(current_user, project_owner, event_filter, params).execute - describe 'issue activity events' do - let(:issue) { create(:issue, project: public_project) } - let(:note) { create(:note_on_issue, noteable: issue, project: public_project) } - let!(:event_a) { create(:event, :commented, target: note, author: project_owner) } - let!(:event_b) { create(:event, :closed, target: issue, author: project_owner) } + expect(events).to include(design_event) + expect(events.size).to eq(1) + end - it 'includes all issue related events', :aggregate_failures do - events = finder.execute + it 'only includes team events', :aggregate_failures do + event_filter = EventFilter.new(EventFilter::TEAM) + events = described_class.new(current_user, project_owner, event_filter, params).execute - expect(events).to include(event_a) - expect(events).to include(event_b) + expect(events).to include(private_event, internal_event, public_event, team_event) + expect(events.size).to eq(4) + end end - end - context 'limits' do - before do - stub_const("#{described_class}::DEFAULT_LIMIT", 1) - stub_const("#{described_class}::MAX_LIMIT", 3) - end + describe 'issue activity events' do + let(:issue) { create(:issue, project: public_project) } + let(:note) { create(:note_on_issue, noteable: issue, project: public_project) } + let!(:event_a) { create(:event, :commented, target: note, author: project_owner) } + let!(:event_b) { create(:event, :closed, target: issue, author: project_owner) } - context 'when limit is not set' do - it 'returns events limited to DEFAULT_LIMIT' do - expect(finder.execute.size).to eq(described_class::DEFAULT_LIMIT) + it 'includes all issue related events', :aggregate_failures do + events = finder.execute + + expect(events).to include(event_a) + expect(events).to include(event_b) end end - context 'when limit is set' do - let(:limit) { 2 } + context 'limits' do + before do + stub_const("#{described_class}::DEFAULT_LIMIT", 1) + stub_const("#{described_class}::MAX_LIMIT", 3) + end - it 'returns events limited to specified limit' do - expect(finder.execute.size).to eq(limit) + context 'when limit is not set' do + it 'returns events limited to DEFAULT_LIMIT' do + expect(finder.execute.size).to eq(described_class::DEFAULT_LIMIT) + end end - end - context 'when limit is set to a number that exceeds maximum limit' do - let(:limit) { 4 } + context 'when limit is set' do + let(:limit) { 2 } - before do - create(:event, project: public_project, author: project_owner) + it 'returns events limited to specified limit' do + expect(finder.execute.size).to eq(limit) + end end - it 'returns events limited to MAX_LIMIT' do - expect(finder.execute.size).to eq(described_class::MAX_LIMIT) + context 'when limit is set to a number that exceeds maximum limit' do + let(:limit) { 4 } + + before do + create(:event, project: public_project, author: project_owner) + end + + it 'returns events limited to MAX_LIMIT' do + expect(finder.execute.size).to eq(described_class::MAX_LIMIT) + end end end end end + + context 'when the optimized_followed_users_queries FF is on' do + before do + stub_feature_flags(optimized_followed_users_queries: true) + end + + it_behaves_like 'UserRecentEventsFinder examples' + end + + context 'when the optimized_followed_users_queries FF is off' do + before do + stub_feature_flags(optimized_followed_users_queries: false) + end + + it_behaves_like 'UserRecentEventsFinder examples' + end end diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb index fab48cf3178..271dce44db7 100644 --- a/spec/finders/users_finder_spec.rb +++ b/spec/finders/users_finder_spec.rb @@ -6,13 +6,15 @@ RSpec.describe UsersFinder do describe '#execute' do include_context 'UsersFinder#execute filter by project context' + let_it_be(:project_bot) { create(:user, :project_bot) } + context 'with a normal user' do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } - it 'returns all users' do + it 'returns searchable users' do users = described_class.new(user).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, external_user, omniauth_user, internal_user, admin_user) + expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'filters by username' do @@ -34,9 +36,9 @@ RSpec.describe UsersFinder do end it 'filters by search' do - users = described_class.new(user, search: 'orando').execute + users = described_class.new(user, search: 'ohndo').execute - expect(users).to contain_exactly(blocked_user) + expect(users).to contain_exactly(normal_user) end it 'does not filter by private emails search' do @@ -45,18 +47,6 @@ RSpec.describe UsersFinder do expect(users).to be_empty end - it 'filters by blocked users' do - users = described_class.new(user, blocked: true).execute - - expect(users).to contain_exactly(blocked_user) - end - - it 'filters by active users' do - users = described_class.new(user, active: true).execute - - expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, admin_user) - end - it 'filters by external users' do users = described_class.new(user, external: true).execute @@ -66,7 +56,7 @@ RSpec.describe UsersFinder do it 'filters by non external users' do users = described_class.new(user, non_external: true).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user, internal_user, admin_user) + expect(users).to contain_exactly(user, normal_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'filters by created_at' do @@ -83,7 +73,7 @@ RSpec.describe UsersFinder do it 'filters by non internal users' do users = described_class.new(user, non_internal: true).execute - expect(users).to contain_exactly(user, normal_user, external_user, blocked_user, omniauth_user, admin_user) + expect(users).to contain_exactly(user, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot) end it 'does not filter by custom attributes' do @@ -92,23 +82,23 @@ RSpec.describe UsersFinder do custom_attributes: { foo: 'bar' } ).execute - expect(users).to contain_exactly(user, normal_user, blocked_user, external_user, omniauth_user, internal_user, admin_user) + expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'orders returned results' do users = described_class.new(user, sort: 'id_asc').execute - expect(users).to eq([normal_user, admin_user, blocked_user, external_user, omniauth_user, internal_user, user]) + expect(users).to eq([normal_user, admin_user, external_user, unconfirmed_user, omniauth_user, internal_user, project_bot, user]) end it 'does not filter by admins' do users = described_class.new(user, admins: true).execute - expect(users).to contain_exactly(user, normal_user, external_user, admin_user, blocked_user, omniauth_user, internal_user) + expect(users).to contain_exactly(user, normal_user, external_user, admin_user, unconfirmed_user, omniauth_user, internal_user, project_bot) end end context 'with an admin user', :enable_admin_mode do - let(:admin) { create(:admin) } + let_it_be(:admin) { create(:admin) } it 'filters by external users' do users = described_class.new(admin, external: true).execute @@ -119,7 +109,19 @@ RSpec.describe UsersFinder do it 'returns all users' do users = described_class.new(admin).execute - expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user, internal_user, admin_user) + expect(users).to contain_exactly(admin, normal_user, blocked_user, unconfirmed_user, banned_user, external_user, omniauth_user, internal_user, admin_user, project_bot) + end + + it 'filters by blocked users' do + users = described_class.new(admin, blocked: true).execute + + expect(users).to contain_exactly(blocked_user) + end + + it 'filters by active users' do + users = described_class.new(admin, active: true).execute + + expect(users).to contain_exactly(admin, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot) end it 'returns only admins' do |