Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/finders')
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb5
-rw-r--r--spec/finders/ci/jobs_finder_spec.rb33
-rw-r--r--spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb99
-rw-r--r--spec/finders/container_repositories_finder_spec.rb30
-rw-r--r--spec/finders/deployments_finder_spec.rb207
-rw-r--r--spec/finders/license_template_finder_spec.rb43
-rw-r--r--spec/finders/merge_request/metrics_finder_spec.rb76
-rw-r--r--spec/finders/merge_requests/oldest_per_commit_finder_spec.rb46
-rw-r--r--spec/finders/packages/group_packages_finder_spec.rb1
-rw-r--r--spec/finders/packages/packages_finder_spec.rb1
-rw-r--r--spec/finders/repositories/commits_with_trailer_finder_spec.rb38
-rw-r--r--spec/finders/repositories/previous_tag_finder_spec.rb41
-rw-r--r--spec/finders/template_finder_spec.rb192
-rw-r--r--spec/finders/terraform/states_finder_spec.rb45
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb120
15 files changed, 866 insertions, 111 deletions
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
index 357b6dfcea2..28bd7e12916 100644
--- a/spec/finders/autocomplete/users_finder_spec.rb
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -118,5 +118,10 @@ RSpec.describe Autocomplete::UsersFinder do
it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) }
end
+
+ it 'preloads the status association' do
+ associations = subject.map { |user| user.association(:status) }
+ expect(associations).to all(be_loaded)
+ end
end
end
diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb
index 4a6585e3f2b..ab056dd26e8 100644
--- a/spec/finders/ci/jobs_finder_spec.rb
+++ b/spec/finders/ci/jobs_finder_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do
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) }
+ let_it_be(:job_3) { create(:ci_build, :success, pipeline: pipeline, name: 'build') }
let(:params) { {} }
@@ -95,4 +95,35 @@ RSpec.describe Ci::JobsFinder, '#execute' do
end
end
end
+
+ context 'when pipeline is present' do
+ before_all do
+ project.add_maintainer(user)
+ job_3.update!(retried: true)
+ end
+
+ let_it_be(:job_4) { create(:ci_build, :success, pipeline: pipeline, name: 'build') }
+
+ subject { described_class.new(current_user: user, pipeline: pipeline, params: params).execute }
+
+ it 'does not return retried jobs by default' do
+ expect(subject).to match_array([job_4])
+ end
+
+ context 'when include_retried is false' do
+ let(:params) { { include_retried: false } }
+
+ it 'does not return retried jobs' do
+ expect(subject).to match_array([job_4])
+ end
+ end
+
+ context 'when include_retried is true' do
+ let(:params) { { include_retried: true } }
+
+ it 'returns retried jobs' do
+ expect(subject).to match_array([job_3, job_4])
+ end
+ end
+ end
end
diff --git a/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb b/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb
new file mode 100644
index 00000000000..a703f3b800c
--- /dev/null
+++ b/spec/finders/ci/testing/daily_build_group_report_results_finder_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Testing::DailyBuildGroupReportResultsFinder do
+ describe '#execute' do
+ let_it_be(:project) { create(:project, :private) }
+ let(:user_without_permission) { create(:user) }
+ let_it_be(:user_with_permission) { project.owner }
+ let_it_be(:ref_path) { 'refs/heads/master' }
+ let(:limit) { nil }
+ let_it_be(:default_branch) { false }
+ let(:start_date) { '2020-03-09' }
+ let(:end_date) { '2020-03-10' }
+ let(:sort) { true }
+
+ let_it_be(:rspec_coverage_1) { create_daily_coverage('rspec', 79.0, '2020-03-09') }
+ let_it_be(:karma_coverage_1) { create_daily_coverage('karma', 89.0, '2020-03-09') }
+ let_it_be(:rspec_coverage_2) { create_daily_coverage('rspec', 95.0, '2020-03-10') }
+ let_it_be(:karma_coverage_2) { create_daily_coverage('karma', 92.0, '2020-03-10') }
+ let_it_be(:rspec_coverage_3) { create_daily_coverage('rspec', 97.0, '2020-03-11') }
+ let_it_be(:karma_coverage_3) { create_daily_coverage('karma', 99.0, '2020-03-11') }
+
+ let(:finder) { described_class.new(params: params, current_user: current_user) }
+
+ let(:params) do
+ {
+ project: project,
+ coverage: true,
+ ref_path: ref_path,
+ start_date: start_date,
+ end_date: end_date,
+ limit: limit,
+ sort: sort
+ }
+ end
+
+ subject(:coverages) { finder.execute }
+
+ context 'when params are provided' do
+ context 'when current user is not allowed to read data' do
+ let(:current_user) { user_without_permission }
+
+ it 'returns an empty collection' do
+ expect(coverages).to be_empty
+ end
+ end
+
+ context 'when current user is allowed to read data' do
+ let(:current_user) { user_with_permission }
+
+ it 'returns matching coverages within the given date range' do
+ expect(coverages).to match_array([
+ karma_coverage_2,
+ rspec_coverage_2,
+ karma_coverage_1,
+ rspec_coverage_1
+ ])
+ end
+
+ context 'when ref_path is nil' do
+ let(:default_branch) { true }
+ let(:ref_path) { nil }
+
+ it 'returns coverages for the default branch' do
+ rspec_coverage_4 = create_daily_coverage('rspec', 66.0, '2020-03-10')
+
+ expect(coverages).to contain_exactly(rspec_coverage_4)
+ end
+ end
+
+ context 'when limit is specified' do
+ let(:limit) { 2 }
+
+ it 'returns limited number of matching coverages within the given date range' do
+ expect(coverages).to match_array([
+ karma_coverage_2,
+ rspec_coverage_2
+ ])
+ end
+ end
+ end
+ end
+ end
+
+ private
+
+ def create_daily_coverage(group_name, coverage, date)
+ create(
+ :ci_daily_build_group_report_result,
+ project: project,
+ ref_path: ref_path || 'feature-branch',
+ group_name: group_name,
+ data: { 'coverage' => coverage },
+ date: date,
+ default_branch: default_branch
+ )
+ end
+end
diff --git a/spec/finders/container_repositories_finder_spec.rb b/spec/finders/container_repositories_finder_spec.rb
index b6305e3f5b7..983f6dba28b 100644
--- a/spec/finders/container_repositories_finder_spec.rb
+++ b/spec/finders/container_repositories_finder_spec.rb
@@ -32,6 +32,34 @@ RSpec.describe ContainerRepositoriesFinder do
end
end
+ shared_examples 'with sorting' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:sort_repository) do
+ create(:container_repository, name: 'bar', project: project, created_at: 1.day.ago)
+ end
+
+ let_it_be(:sort_repository2) do
+ create(:container_repository, name: 'foo', project: project, created_at: 1.hour.ago, updated_at: 1.hour.ago)
+ end
+
+ [:created_desc, :updated_asc, :name_desc].each do |order|
+ context "with sort set to #{order}" do
+ let(:params) { { sort: order } }
+
+ it { is_expected.to eq([sort_repository2, sort_repository])}
+ end
+ end
+
+ [:created_asc, :updated_desc, :name_asc].each do |order|
+ context "with sort set to #{order}" do
+ let(:params) { { sort: order } }
+
+ it { is_expected.to eq([sort_repository, sort_repository2])}
+ end
+ end
+ end
+
describe '#execute' do
context 'with authorized user' do
subject { described_class.new(user: reporter, subject: subject_object, params: params).execute }
@@ -47,6 +75,7 @@ RSpec.describe ContainerRepositoriesFinder do
it { is_expected.to match_array([project_repository, other_repository]) }
it_behaves_like 'with name search'
+ it_behaves_like 'with sorting'
end
context 'when subject_type is project' do
@@ -55,6 +84,7 @@ RSpec.describe ContainerRepositoriesFinder do
it { is_expected.to match_array([project_repository]) }
it_behaves_like 'with name search'
+ it_behaves_like 'with sorting'
end
context 'with invalid subject_type' do
diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb
index e4e0f366eeb..0f659fa1dab 100644
--- a/spec/finders/deployments_finder_spec.rb
+++ b/spec/finders/deployments_finder_spec.rb
@@ -3,130 +3,161 @@
require 'spec_helper'
RSpec.describe DeploymentsFinder do
- subject { described_class.new(project, params).execute }
-
- let(:project) { create(:project, :public, :test_repo) }
- let(:params) { {} }
+ subject { described_class.new(params).execute }
describe "#execute" do
- it 'returns all deployments by default' do
- deployments = create_list(:deployment, 2, :success, project: project)
- is_expected.to match_array(deployments)
+ context 'when project or group is missing' do
+ let(:params) { {} }
+
+ it 'returns nothing' do
+ is_expected.to eq([])
+ end
end
- describe 'filtering' do
- context 'when updated_at filters are specified' do
- let(:params) { { updated_before: 1.day.ago, updated_after: 3.days.ago } }
- let!(:deployment_1) { create(:deployment, :success, project: project, updated_at: 2.days.ago) }
- let!(:deployment_2) { create(:deployment, :success, project: project, updated_at: 4.days.ago) }
- let!(:deployment_3) { create(:deployment, :success, project: project, updated_at: 1.hour.ago) }
+ context 'at project scope' do
+ let_it_be(:project) { create(:project, :public, :test_repo) }
+ let(:base_params) { { project: project } }
+
+ describe 'filtering' do
+ context 'when updated_at filters are specified' do
+ let(:params) { { **base_params, updated_before: 1.day.ago, updated_after: 3.days.ago } }
+ let!(:deployment_1) { create(:deployment, :success, project: project, updated_at: 2.days.ago) }
+ let!(:deployment_2) { create(:deployment, :success, project: project, updated_at: 4.days.ago) }
+ let!(:deployment_3) { create(:deployment, :success, project: project, updated_at: 1.hour.ago) }
- it 'returns deployments with matched updated_at' do
- is_expected.to match_array([deployment_1])
+ it 'returns deployments with matched updated_at' do
+ is_expected.to match_array([deployment_1])
+ end
end
- end
- context 'when the environment name is specified' do
- let!(:environment1) { create(:environment, project: project) }
- let!(:environment2) { create(:environment, project: project) }
- let!(:deployment1) do
- create(:deployment, project: project, environment: environment1)
+ context 'when the environment name is specified' do
+ let!(:environment1) { create(:environment, project: project) }
+ let!(:environment2) { create(:environment, project: project) }
+ let!(:deployment1) do
+ create(:deployment, project: project, environment: environment1)
+ end
+
+ let!(:deployment2) do
+ create(:deployment, project: project, environment: environment2)
+ end
+
+ let(:params) { { **base_params, environment: environment1.name } }
+
+ it 'returns deployments for the given environment' do
+ is_expected.to match_array([deployment1])
+ end
end
- let!(:deployment2) do
- create(:deployment, project: project, environment: environment2)
+ context 'when the deployment status is specified' do
+ let!(:deployment1) { create(:deployment, :success, project: project) }
+ let!(:deployment2) { create(:deployment, :failed, project: project) }
+ let(:params) { { **base_params, status: 'success' } }
+
+ it 'returns deployments for the given environment' do
+ is_expected.to match_array([deployment1])
+ end
end
- let(:params) { { environment: environment1.name } }
+ context 'when using an invalid deployment status' do
+ let(:params) { { **base_params, status: 'kittens' } }
- it 'returns deployments for the given environment' do
- is_expected.to match_array([deployment1])
+ it 'raises ArgumentError' do
+ expect { subject }.to raise_error(ArgumentError)
+ end
end
end
- context 'when the deployment status is specified' do
- let!(:deployment1) { create(:deployment, :success, project: project) }
- let!(:deployment2) { create(:deployment, :failed, project: project) }
- let(:params) { { status: 'success' } }
+ describe 'ordering' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:params) { { **base_params, order_by: order_by, sort: sort } }
+
+ let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) }
+ let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) }
+ let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) }
+
+ where(:order_by, :sort, :ordered_deployments) do
+ 'created_at' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
+ 'created_at' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
+ 'id' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
+ 'id' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
+ 'iid' | 'asc' | [:deployment_3, :deployment_1, :deployment_2]
+ 'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3]
+ 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3]
+ 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2]
+ 'updated_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1]
+ 'updated_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2]
+ 'finished_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1]
+ 'finished_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2]
+ 'invalid' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
+ 'iid' | 'err' | [:deployment_3, :deployment_1, :deployment_2]
+ end
- it 'returns deployments for the given environment' do
- is_expected.to match_array([deployment1])
+ with_them do
+ it 'returns the deployments ordered' do
+ expect(subject).to eq(ordered_deployments.map { |name| public_send(name) })
+ end
end
end
- context 'when using an invalid deployment status' do
- let(:params) { { status: 'kittens' } }
+ describe 'transform `created_at` sorting to `id` sorting' do
+ let(:params) { { **base_params, order_by: 'created_at', sort: 'asc' } }
- it 'raises ArgumentError' do
- expect { subject }.to raise_error(ArgumentError)
+ it 'sorts by only one column' do
+ expect(subject.order_values.size).to eq(1)
end
- end
- end
- describe 'ordering' do
- using RSpec::Parameterized::TableSyntax
-
- let(:params) { { order_by: order_by, sort: sort } }
-
- let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now) }
- let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago) }
- let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago) }
-
- where(:order_by, :sort, :ordered_deployments) do
- 'created_at' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
- 'created_at' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
- 'id' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
- 'id' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
- 'iid' | 'asc' | [:deployment_3, :deployment_1, :deployment_2]
- 'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3]
- 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3]
- 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2]
- 'updated_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1]
- 'updated_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2]
- 'invalid' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
- 'iid' | 'err' | [:deployment_3, :deployment_1, :deployment_2]
+ it 'sorts by `id`' do
+ expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql)
+ end
end
- with_them do
- it 'returns the deployments ordered' do
- expect(subject).to eq(ordered_deployments.map { |name| public_send(name) })
+ describe 'tie-breaker for `finished_at` sorting' do
+ let(:params) { { **base_params, order_by: 'updated_at', sort: 'asc' } }
+
+ it 'sorts by two columns' do
+ expect(subject.order_values.size).to eq(2)
end
- end
- end
- describe 'transform `created_at` sorting to `id` sorting' do
- let(:params) { { order_by: 'created_at', sort: 'asc' } }
+ it 'adds `id` sorting as the second order column' do
+ order_value = subject.order_values[1]
- it 'sorts by only one column' do
- expect(subject.order_values.size).to eq(1)
- end
+ expect(order_value.to_sql).to eq(Deployment.arel_table[:id].desc.to_sql)
+ end
- it 'sorts by `id`' do
- expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql)
- end
- end
+ it 'uses the `id DESC` as tie-breaker when ordering' do
+ updated_at = Time.now
- describe 'tie-breaker for `updated_at` sorting' do
- let(:params) { { order_by: 'updated_at', sort: 'asc' } }
+ deployment_1 = create(:deployment, :success, project: project, updated_at: updated_at)
+ deployment_2 = create(:deployment, :success, project: project, updated_at: updated_at)
+ deployment_3 = create(:deployment, :success, project: project, updated_at: updated_at)
- it 'sorts by two columns' do
- expect(subject.order_values.size).to eq(2)
+ expect(subject).to eq([deployment_3, deployment_2, deployment_1])
+ end
end
- it 'adds `id` sorting as the second order column' do
- order_value = subject.order_values[1]
+ context 'when filtering by finished time' do
+ let!(:deployment_1) { create(:deployment, :success, project: project, finished_at: 2.days.ago) }
+ let!(:deployment_2) { create(:deployment, :success, project: project, finished_at: 4.days.ago) }
+ let!(:deployment_3) { create(:deployment, :success, project: project, finished_at: 5.hours.ago) }
- expect(order_value.to_sql).to eq(Deployment.arel_table[:id].desc.to_sql)
- end
+ context 'when filtering by finished_after and finished_before' do
+ let(:params) { { **base_params, finished_after: 3.days.ago, finished_before: 1.day.ago } }
+
+ it { is_expected.to match_array([deployment_1]) }
+ end
- it 'uses the `id DESC` as tie-breaker when ordering' do
- updated_at = Time.now
+ context 'when the finished_before parameter is missing' do
+ let(:params) { { **base_params, finished_after: 3.days.ago } }
- deployment_1 = create(:deployment, :success, project: project, updated_at: updated_at)
- deployment_2 = create(:deployment, :success, project: project, updated_at: updated_at)
- deployment_3 = create(:deployment, :success, project: project, updated_at: updated_at)
+ it { is_expected.to match_array([deployment_1, deployment_3]) }
+ end
+
+ context 'when finished_after is missing' do
+ let(:params) { { **base_params, finished_before: 3.days.ago } }
- expect(subject).to eq([deployment_3, deployment_2, deployment_1])
+ it { is_expected.to match_array([deployment_2]) }
+ end
end
end
end
diff --git a/spec/finders/license_template_finder_spec.rb b/spec/finders/license_template_finder_spec.rb
index 93f13632b6f..754b92faccc 100644
--- a/spec/finders/license_template_finder_spec.rb
+++ b/spec/finders/license_template_finder_spec.rb
@@ -3,12 +3,7 @@
require 'spec_helper'
RSpec.describe LicenseTemplateFinder do
- describe '#execute' do
- subject(:result) { described_class.new(nil, params).execute }
-
- let(:categories) { categorised_licenses.keys }
- let(:categorised_licenses) { result.group_by(&:category) }
-
+ RSpec.shared_examples 'filters by popular category' do
context 'popular: true' do
let(:params) { { popular: true } }
@@ -26,6 +21,15 @@ RSpec.describe LicenseTemplateFinder do
expect(categorised_licenses[:Other]).to be_present
end
end
+ end
+
+ describe '#execute' do
+ subject(:result) { described_class.new(nil, params).execute }
+
+ let(:categories) { categorised_licenses.keys }
+ let(:categorised_licenses) { result.group_by(&:category) }
+
+ it_behaves_like 'filters by popular category'
context 'popular: nil' do
let(:params) { { popular: nil } }
@@ -48,4 +52,31 @@ RSpec.describe LicenseTemplateFinder do
end
end
end
+
+ describe '#template_names' do
+ let(:params) { {} }
+
+ subject(:template_names) { described_class.new(nil, params).template_names }
+
+ let(:categories) { categorised_licenses.keys }
+ let(:categorised_licenses) { template_names }
+
+ it_behaves_like 'filters by popular category'
+
+ context 'popular: nil' do
+ let(:params) { { popular: nil } }
+
+ it 'returns all licenses known by the Licensee gem' do
+ from_licensee = Licensee::License.all.map { |l| l.key }
+
+ expect(template_names.values.flatten.map { |x| x[:key] }).to match_array(from_licensee)
+ end
+ end
+
+ context 'template names hash keys' do
+ it 'has all the expected keys' do
+ expect(template_names.values.flatten.first.keys).to match_array(%i(id key name project_id))
+ end
+ end
+ end
end
diff --git a/spec/finders/merge_request/metrics_finder_spec.rb b/spec/finders/merge_request/metrics_finder_spec.rb
new file mode 100644
index 00000000000..ea039462e66
--- /dev/null
+++ b/spec/finders/merge_request/metrics_finder_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequest::MetricsFinder do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:merge_request_not_merged) { create(:merge_request, :unique_branches, source_project: project) }
+ let_it_be(:merged_at) { Time.new(2020, 5, 1) }
+ let_it_be(:merge_request_merged) do
+ create(:merge_request, :unique_branches, :merged, source_project: project).tap do |mr|
+ mr.metrics.update!(merged_at: merged_at)
+ end
+ end
+
+ let(:params) do
+ {
+ target_project: project,
+ merged_after: merged_at - 10.days,
+ merged_before: merged_at + 10.days
+ }
+ end
+
+ subject { described_class.new(current_user, params).execute.to_a }
+
+ context 'when target project is missing' do
+ before do
+ params.delete(:target_project)
+ end
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when the user is not part of the project' do
+ it { is_expected.to be_empty }
+ end
+
+ context 'when user is part of the project' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'returns merge request records' do
+ is_expected.to eq([merge_request_merged.metrics])
+ end
+
+ it 'excludes not merged records' do
+ is_expected.not_to eq([merge_request_not_merged.metrics])
+ end
+
+ context 'when only merged_before is given' do
+ before do
+ params.delete(:merged_after)
+ end
+
+ it { is_expected.to eq([merge_request_merged.metrics]) }
+ end
+
+ context 'when only merged_after is given' do
+ before do
+ params.delete(:merged_before)
+ end
+
+ it { is_expected.to eq([merge_request_merged.metrics]) }
+ end
+
+ context 'when no records matching the date range' do
+ before do
+ params[:merged_before] = merged_at - 1.year
+ params[:merged_after] = merged_at - 2.years
+ end
+
+ it { is_expected.to be_empty }
+ end
+ end
+end
diff --git a/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb
new file mode 100644
index 00000000000..4e9d021fa5d
--- /dev/null
+++ b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequests::OldestPerCommitFinder do
+ describe '#execute' do
+ it 'returns a Hash mapping commit SHAs to their oldest merge requests' do
+ project = create(:project)
+ mr1 = create(:merge_request, :merged, target_project: project)
+ mr2 = create(:merge_request, :merged, target_project: project)
+ mr1_diff = create(:merge_request_diff, merge_request: mr1)
+ mr2_diff = create(:merge_request_diff, merge_request: mr2)
+ sha1 = Digest::SHA1.hexdigest('foo')
+ sha2 = Digest::SHA1.hexdigest('bar')
+
+ create(:merge_request_diff_commit, merge_request_diff: mr1_diff, sha: sha1)
+ create(:merge_request_diff_commit, merge_request_diff: mr2_diff, sha: sha1)
+ create(
+ :merge_request_diff_commit,
+ merge_request_diff: mr2_diff,
+ sha: sha2,
+ relative_order: 1
+ )
+
+ commits = [double(:commit, id: sha1), double(:commit, id: sha2)]
+
+ expect(described_class.new(project).execute(commits)).to eq(
+ sha1 => mr1,
+ sha2 => mr2
+ )
+ end
+
+ it 'skips merge requests that are not merged' do
+ mr = create(:merge_request)
+ mr_diff = create(:merge_request_diff, merge_request: mr)
+ sha = Digest::SHA1.hexdigest('foo')
+
+ create(:merge_request_diff_commit, merge_request_diff: mr_diff, sha: sha)
+
+ commits = [double(:commit, id: sha)]
+
+ expect(described_class.new(mr.target_project).execute(commits))
+ .to be_empty
+ end
+ end
+end
diff --git a/spec/finders/packages/group_packages_finder_spec.rb b/spec/finders/packages/group_packages_finder_spec.rb
index 8dd53b9c3f9..445482a5a96 100644
--- a/spec/finders/packages/group_packages_finder_spec.rb
+++ b/spec/finders/packages/group_packages_finder_spec.rb
@@ -147,6 +147,7 @@ RSpec.describe Packages::GroupPackagesFinder do
end
it_behaves_like 'concerning versionless param'
+ it_behaves_like 'concerning package statuses'
end
context 'group has package of all types' do
diff --git a/spec/finders/packages/packages_finder_spec.rb b/spec/finders/packages/packages_finder_spec.rb
index 77a171db144..6e92616bafa 100644
--- a/spec/finders/packages/packages_finder_spec.rb
+++ b/spec/finders/packages/packages_finder_spec.rb
@@ -82,5 +82,6 @@ RSpec.describe ::Packages::PackagesFinder do
end
it_behaves_like 'concerning versionless param'
+ it_behaves_like 'concerning package statuses'
end
end
diff --git a/spec/finders/repositories/commits_with_trailer_finder_spec.rb b/spec/finders/repositories/commits_with_trailer_finder_spec.rb
new file mode 100644
index 00000000000..0c457aae340
--- /dev/null
+++ b/spec/finders/repositories/commits_with_trailer_finder_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Repositories::CommitsWithTrailerFinder do
+ let(:project) { create(:project, :repository) }
+
+ describe '#each_page' do
+ it 'only yields commits with the given trailer' do
+ finder = described_class.new(
+ project: project,
+ from: '570e7b2abdd848b95f2f578043fc23bd6f6fd24d',
+ to: 'c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd'
+ )
+
+ commits = finder.each_page('Signed-off-by').to_a.flatten
+
+ expect(commits.length).to eq(1)
+ expect(commits.first.id).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ expect(commits.first.trailers).to eq(
+ 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>'
+ )
+ end
+
+ it 'supports paginating of commits' do
+ finder = described_class.new(
+ project: project,
+ from: 'c1acaa58bbcbc3eafe538cb8274ba387047b69f8',
+ to: '5937ac0a7beb003549fc5fd26fc247adbce4a52e',
+ per_page: 1
+ )
+
+ commits = finder.each_page('Signed-off-by')
+
+ expect(commits.count).to eq(4)
+ end
+ end
+end
diff --git a/spec/finders/repositories/previous_tag_finder_spec.rb b/spec/finders/repositories/previous_tag_finder_spec.rb
new file mode 100644
index 00000000000..7cc33d11baf
--- /dev/null
+++ b/spec/finders/repositories/previous_tag_finder_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Repositories::PreviousTagFinder do
+ let(:project) { build_stubbed(:project) }
+ let(:finder) { described_class.new(project) }
+
+ describe '#execute' do
+ context 'when there is a previous tag' do
+ it 'returns the previous tag' do
+ tag1 = double(:tag1, name: 'v1.0.0')
+ tag2 = double(:tag2, name: 'v1.1.0')
+ tag3 = double(:tag3, name: 'v2.0.0')
+ tag4 = double(:tag4, name: '1.0.0')
+
+ allow(project.repository)
+ .to receive(:tags)
+ .and_return([tag1, tag3, tag2, tag4])
+
+ expect(finder.execute('2.1.0')).to eq(tag3)
+ expect(finder.execute('2.0.0')).to eq(tag2)
+ expect(finder.execute('1.5.0')).to eq(tag2)
+ expect(finder.execute('1.0.1')).to eq(tag1)
+ end
+ end
+
+ context 'when there is no previous tag' do
+ it 'returns nil' do
+ tag1 = double(:tag1, name: 'v1.0.0')
+ tag2 = double(:tag2, name: 'v1.1.0')
+
+ allow(project.repository)
+ .to receive(:tags)
+ .and_return([tag1, tag2])
+
+ expect(finder.execute('1.0.0')).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb
index 2da864b9a46..164975fdfb6 100644
--- a/spec/finders/template_finder_spec.rb
+++ b/spec/finders/template_finder_spec.rb
@@ -5,6 +5,102 @@ require 'spec_helper'
RSpec.describe TemplateFinder do
using RSpec::Parameterized::TableSyntax
+ let_it_be(:template_files) do
+ {
+ "Dockerfile/project_dockerfiles_template.dockerfile" => "project_dockerfiles_template content",
+ "gitignore/project_gitignores_template.gitignore" => "project_gitignores_template content",
+ "gitlab-ci/project_gitlab_ci_ymls_template.yml" => "project_gitlab_ci_ymls_template content",
+ ".gitlab/issue_templates/project_issues_template.md" => "project_issues_template content",
+ ".gitlab/merge_request_templates/project_merge_requests_template.md" => "project_merge_requests_template content"
+ }
+ end
+
+ RSpec.shared_examples 'fetches predefined vendor templates' do
+ where(:type, :vendored_name) do
+ :dockerfiles | 'Binary'
+ :gitignores | 'Actionscript'
+ :gitlab_ci_ymls | 'Android'
+ :metrics_dashboard_ymls | 'Default'
+ :gitlab_ci_syntax_ymls | 'Artifacts example'
+ end
+
+ with_them do
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to include(have_attributes(name: vendored_name))
+ end
+
+ context 'with name param' do
+ let(:params) { { name: vendored_name } }
+
+ it 'returns only the specified vendored template when a name is specified' do
+ expect(result).to have_attributes(name: vendored_name)
+ end
+
+ context 'with mistaken name param' do
+ let(:params) { { name: 'unknown' } }
+
+ it 'returns nil when an unknown name is specified' do
+ expect(result).to be_nil
+ end
+ end
+ end
+ end
+ end
+
+ RSpec.shared_examples 'no issues and merge requests templates available' do
+ context 'with issue and merge request templates' do
+ where(:type, :vendored_name) do
+ :issues | nil
+ :merge_requests | nil
+ end
+
+ with_them do
+ context 'when fetching all templates' do
+ it 'returns empty array' do
+ expect(result).to eq([])
+ end
+ end
+
+ context 'when looking for specific template by name' do
+ let(:params) { { name: 'anything' } }
+
+ it 'raises an error' do
+ expect { result }.to raise_exception(Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError)
+ end
+ end
+ end
+ end
+ end
+
+ RSpec.shared_examples 'fetches issues and merge requests templates' do
+ where(:type, :template_name) do
+ :issues | 'project_issues_template'
+ :merge_requests | 'project_merge_requests_template'
+ end
+
+ with_them do
+ it 'returns all repository template files for issues and merge requests' do
+ expect(result).to include(have_attributes(name: template_name))
+ end
+
+ context 'with name param' do
+ let(:params) { { name: template_name } }
+
+ it 'returns only the specified vendored template when a name is specified' do
+ expect(result).to have_attributes(name: template_name)
+ end
+
+ context 'with mistaken name param' do
+ let(:params) { { name: 'unknown' } }
+
+ it 'raises an error when an unknown name is specified' do
+ expect { result }.to raise_exception(Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError)
+ end
+ end
+ end
+ end
+ end
+
describe '#build' do
let(:project) { build_stubbed(:project) }
@@ -15,6 +111,8 @@ RSpec.describe TemplateFinder do
:licenses | ::LicenseTemplateFinder
:metrics_dashboard_ymls | described_class
:gitlab_ci_syntax_ymls | described_class
+ :issues | described_class
+ :merge_requests | described_class
end
with_them do
@@ -26,6 +124,37 @@ RSpec.describe TemplateFinder do
end
describe '#execute' do
+ let_it_be(:project) { nil }
+ let(:params) { {} }
+
+ subject(:result) { described_class.new(type, project, params).execute }
+
+ context 'when no project is passed in' do
+ it_behaves_like 'fetches predefined vendor templates'
+ it_behaves_like 'no issues and merge requests templates available'
+ end
+
+ context 'when project has no repository' do
+ let_it_be(:project) { create(:project) }
+
+ it_behaves_like 'fetches predefined vendor templates'
+ it_behaves_like 'no issues and merge requests templates available'
+ end
+
+ context 'when project has a repository' do
+ let_it_be(:project) { create(:project, :custom_repo, files: template_files) }
+
+ it_behaves_like 'fetches predefined vendor templates'
+ it_behaves_like 'fetches issues and merge requests templates'
+ end
+ end
+
+ describe '#template_names' do
+ let_it_be(:project) { nil }
+ let(:params) { {} }
+
+ subject(:result) { described_class.new(type, project, params).template_names.values.flatten.map { |el| OpenStruct.new(el) } }
+
where(:type, :vendored_name) do
:dockerfiles | 'Binary'
:gitignores | 'Actionscript'
@@ -35,22 +164,67 @@ RSpec.describe TemplateFinder do
end
with_them do
- it 'returns all vendored templates when no name is specified' do
- result = described_class.new(type, nil).execute
+ context 'when no project is passed in' do
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to include(have_attributes(name: vendored_name))
+ end
+ end
- expect(result).to include(have_attributes(name: vendored_name))
+ context 'when project has no repository' do
+ let_it_be(:project) { create(:project) }
+
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to include(have_attributes(name: vendored_name))
+ end
end
- it 'returns only the specified vendored template when a name is specified' do
- result = described_class.new(type, nil, name: vendored_name).execute
+ context 'when project has a repository' do
+ let_it_be(:project) { create(:project, :custom_repo, files: template_files) }
- expect(result).to have_attributes(name: vendored_name)
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to include(have_attributes(name: vendored_name))
+ end
end
- it 'returns nil when an unknown name is specified' do
- result = described_class.new(type, nil, name: 'unknown').execute
+ context 'template names hash keys' do
+ it 'has all the expected keys' do
+ expect(result.first.to_h.keys).to match_array(%i(id key name project_id))
+ end
+ end
+ end
+
+ where(:type, :template_name) do
+ :issues | 'project_issues_template'
+ :merge_requests | 'project_merge_requests_template'
+ end
+
+ with_them do
+ context 'when no project is passed in' do
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to eq([])
+ end
+ end
+
+ context 'when project has no repository' do
+ let_it_be(:project) { create(:project) }
+
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to eq([])
+ end
+ end
+
+ context 'when project has a repository' do
+ let_it_be(:project) { create(:project, :custom_repo, files: template_files) }
+
+ it 'returns all vendored templates when no name is specified' do
+ expect(result).to include(have_attributes(name: template_name))
+ end
- expect(result).to be_nil
+ context 'template names hash keys' do
+ it 'has all the expected keys' do
+ expect(result.first.to_h.keys).to match_array(%i(id key name project_id))
+ end
+ end
end
end
end
diff --git a/spec/finders/terraform/states_finder_spec.rb b/spec/finders/terraform/states_finder_spec.rb
new file mode 100644
index 00000000000..260e5f4818f
--- /dev/null
+++ b/spec/finders/terraform/states_finder_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Terraform::StatesFinder do
+ describe '#execute' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:state_1) { create(:terraform_state, project: project) }
+ let_it_be(:state_2) { create(:terraform_state, project: project) }
+
+ let(:user) { project.creator }
+
+ subject { described_class.new(project, user).execute }
+
+ it { is_expected.to contain_exactly(state_1, state_2) }
+
+ context 'user does not have permission' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'filtering by name' do
+ let(:params) { { name: name_param } }
+
+ subject { described_class.new(project, user, params: params).execute }
+
+ context 'name does not match' do
+ let(:name_param) { 'other-name' }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'name does match' do
+ let(:name_param) { state_1.name }
+
+ it { is_expected.to contain_exactly(state_1) }
+ end
+ end
+ end
+end
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index ddba9b595a4..5a9243d150d 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -5,16 +5,17 @@ require 'spec_helper'
RSpec.describe UserRecentEventsFinder do
let_it_be(:project_owner, reload: true) { create(:user) }
let_it_be(:current_user, reload: true) { create(:user) }
- let(:private_project) { create(:project, :private, creator: project_owner) }
- let(:internal_project) { create(:project, :internal, creator: project_owner) }
- let(:public_project) { create(:project, :public, creator: project_owner) }
+ 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(:issue) { create(:issue, project: public_project) }
let(:limit) { nil }
let(:params) { { limit: limit } }
- subject(:finder) { described_class.new(current_user, project_owner, params) }
+ subject(:finder) { described_class.new(current_user, project_owner, nil, params) }
describe '#execute' do
context 'when profile is public' do
@@ -39,11 +40,116 @@ RSpec.describe UserRecentEventsFinder do
expect(finder.execute).to be_empty
end
- describe 'design activity events' do
- let_it_be(:event_a) { create(:design_event, author: project_owner) }
- let_it_be(:event_b) { create(:design_event, author: project_owner) }
+ 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) }
+
+ 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
+
+ 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
+
+ expect(events).to include(private_event, internal_event, public_event)
+ expect(events.size).to eq(3)
+ 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
+
+ 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
+
+ expect(events).to include(push_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
+
+ expect(events).to include(merge_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
+
+ expect(events).to include(issue_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
+
+ expect(events).to include(comment_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
+
+ expect(events).to include(wiki_event)
+ expect(events.size).to eq(1)
+ 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
+
+ expect(events).to include(design_event)
+ expect(events.size).to eq(1)
+ end
+
+ 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(private_event, internal_event, public_event, team_event)
+ expect(events.size).to eq(4)
+ end
+ 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) }
+
+ it 'includes all issue related events', :aggregate_failures do
events = finder.execute
expect(events).to include(event_a)