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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-20 21:38:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-20 21:38:24 +0300
commit983a0bba5d2a042c4a3bbb22432ec192c7501d82 (patch)
treeb153cd387c14ba23bd5a07514c7c01fddf6a78a0 /spec/models
parenta2bddee2cdb38673df0e004d5b32d9f77797de64 (diff)
Add latest changes from gitlab-org/gitlab@12-10-stable-ee
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ci/bridge_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb108
-rw-r--r--spec/models/ci/job_artifact_spec.rb21
-rw-r--r--spec/models/ci/processable_spec.rb78
-rw-r--r--spec/models/ci/runner_spec.rb30
-rw-r--r--spec/models/clusters/applications/fluentd_spec.rb50
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb6
-rw-r--r--spec/models/clusters/cluster_spec.rb3
-rw-r--r--spec/models/concerns/issuable_spec.rb34
-rw-r--r--spec/models/cycle_analytics/group_level_spec.rb2
-rw-r--r--spec/models/diff_note_position_spec.rb7
-rw-r--r--spec/models/import_failure_spec.rb23
-rw-r--r--spec/models/jira_import_state_spec.rb20
-rw-r--r--spec/models/merge_request_diff_spec.rb39
-rw-r--r--spec/models/merge_request_spec.rb66
-rw-r--r--spec/models/metrics/dashboard/annotation_spec.rb26
-rw-r--r--spec/models/project_feature_spec.rb4
-rw-r--r--spec/models/project_import_state_spec.rb21
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb44
-rw-r--r--spec/models/project_spec.rb40
-rw-r--r--spec/models/resource_milestone_event_spec.rb26
-rw-r--r--spec/models/terraform/state_spec.rb25
-rw-r--r--spec/models/user_spec.rb44
-rw-r--r--spec/models/user_type_enums_spec.rb13
24 files changed, 600 insertions, 132 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 31e13122b95..34f89d9cdae 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -17,8 +17,6 @@ describe Ci::Bridge do
{ trigger: { project: 'my/project', branch: 'master' } }
end
- it { is_expected.to include_module(Ci::PipelineDelegator) }
-
it 'has many sourced pipelines' do
expect(bridge).to have_many(:sourced_pipelines)
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 673b9e5f076..bdaecea2089 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -37,8 +37,6 @@ describe Ci::Build do
it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) }
it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
- it { is_expected.to include_module(Ci::PipelineDelegator) }
-
describe 'associations' do
it 'has a bidirectional relationship with projects' do
expect(described_class.reflect_on_association(:project).has_inverse?).to eq(:builds)
@@ -1818,64 +1816,65 @@ describe Ci::Build do
end
describe '#merge_request' do
- def create_mr(build, pipeline, factory: :merge_request, created_at: Time.now)
- create(factory, source_project: pipeline.project,
- target_project: pipeline.project,
- source_branch: build.ref,
- created_at: created_at)
- end
+ subject { pipeline.builds.take.merge_request }
- context 'when a MR has a reference to the pipeline' do
- before do
- @merge_request = create_mr(build, pipeline, factory: :merge_request)
+ context 'on a branch pipeline' do
+ let!(:pipeline) { create(:ci_pipeline, :with_job, project: project, ref: 'fix') }
- commits = [double(id: pipeline.sha)]
- allow(@merge_request).to receive(:commits).and_return(commits)
- allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
+ context 'with no merge request' do
+ it { is_expected.to be_nil }
end
- it 'returns the single associated MR' do
- expect(build.merge_request.id).to eq(@merge_request.id)
- end
- end
+ context 'with an open merge request from the same ref name' do
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'fix') }
- context 'when there is not a MR referencing the pipeline' do
- it 'returns nil' do
- expect(build.merge_request).to be_nil
- end
- end
+ # If no diff exists, the pipeline commit was not part of the merge
+ # request and may have simply incidentally used the same ref name.
+ context 'without a merge request diff containing the pipeline commit' do
+ it { is_expected.to be_nil }
+ end
- context 'when more than one MR have a reference to the pipeline' do
- before do
- @merge_request = create_mr(build, pipeline, factory: :merge_request)
- @merge_request.close!
- @merge_request2 = create_mr(build, pipeline, factory: :merge_request)
+ # If the merge request was truly opened from the branch that the
+ # pipeline ran on, that head sha will be present in a diff.
+ context 'with a merge request diff containing the pipeline commit' do
+ let!(:mr_diff) { create(:merge_request_diff, merge_request: merge_request) }
+ let!(:mr_diff_commit) { create(:merge_request_diff_commit, sha: build.sha, merge_request_diff: mr_diff) }
- commits = [double(id: pipeline.sha)]
- allow(@merge_request).to receive(:commits).and_return(commits)
- allow(@merge_request2).to receive(:commits).and_return(commits)
- allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request, @merge_request2])
+ it { is_expected.to eq(merge_request) }
+ end
end
- it 'returns the first MR' do
- expect(build.merge_request.id).to eq(@merge_request.id)
+ context 'with multiple open merge requests' do
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'fix') }
+ let!(:mr_diff) { create(:merge_request_diff, merge_request: merge_request) }
+ let!(:mr_diff_commit) { create(:merge_request_diff_commit, sha: build.sha, merge_request_diff: mr_diff) }
+
+ let!(:new_merge_request) { create(:merge_request, source_project: project, source_branch: 'fix', target_branch: 'staging') }
+ let!(:new_mr_diff) { create(:merge_request_diff, merge_request: new_merge_request) }
+ let!(:new_mr_diff_commit) { create(:merge_request_diff_commit, sha: build.sha, merge_request_diff: new_mr_diff) }
+
+ it 'returns the first merge request' do
+ expect(subject).to eq(merge_request)
+ end
end
end
- context 'when a Build is created after the MR' do
- before do
- @merge_request = create_mr(build, pipeline, factory: :merge_request_with_diffs)
- pipeline2 = create(:ci_pipeline, project: project)
- @build2 = create(:ci_build, pipeline: pipeline2)
+ context 'on a detached merged request pipeline' do
+ let(:pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, :with_job) }
- allow(@merge_request).to receive(:commit_shas)
- .and_return([pipeline.sha, pipeline2.sha])
- allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
- end
+ it { is_expected.to eq(pipeline.merge_request) }
+ end
- it 'returns the current MR' do
- expect(@build2.merge_request.id).to eq(@merge_request.id)
- end
+ context 'on a legacy detached merged request pipeline' do
+ let(:pipeline) { create(:ci_pipeline, :legacy_detached_merge_request_pipeline, :with_job) }
+
+ it { is_expected.to eq(pipeline.merge_request) }
+ end
+
+ context 'on a pipeline for merged results' do
+ let(:pipeline) { create(:ci_pipeline, :merged_result_pipeline, :with_job) }
+
+ it { is_expected.to eq(pipeline.merge_request) }
end
end
@@ -2281,6 +2280,7 @@ describe Ci::Build do
{ key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true, masked: false },
{ key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false, masked: true },
{ key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false, masked: false },
+ { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true, masked: false },
{ key: 'CI_JOB_STAGE', value: 'test', public: true, masked: false },
{ key: 'CI_NODE_TOTAL', value: '1', public: true, masked: false },
@@ -2333,23 +2333,36 @@ describe Ci::Build do
end
before do
+ allow(Gitlab::Ci::Jwt).to receive(:for_build).with(build).and_return('ci.job.jwt')
build.set_token('my-token')
build.yaml_variables = []
end
it { is_expected.to eq(predefined_variables) }
+ context 'when ci_job_jwt feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_job_jwt: false)
+ end
+
+ it 'CI_JOB_JWT is not included' do
+ expect(subject.pluck(:key)).not_to include('CI_JOB_JWT')
+ end
+ end
+
describe 'variables ordering' do
context 'when variables hierarchy is stubbed' do
let(:build_pre_var) { { key: 'build', value: 'value', public: true, masked: false } }
let(:project_pre_var) { { key: 'project', value: 'value', public: true, masked: false } }
let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } }
let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
+ let(:job_jwt_var) { { key: 'CI_JOB_JWT', value: 'ci.job.jwt', public: false, masked: true } }
before do
allow(build).to receive(:predefined_variables) { [build_pre_var] }
allow(build).to receive(:yaml_variables) { [build_yaml_var] }
allow(build).to receive(:persisted_variables) { [] }
+ allow(build).to receive(:job_jwt_variables) { [job_jwt_var] }
allow_any_instance_of(Project)
.to receive(:predefined_variables) { [project_pre_var] }
@@ -2362,7 +2375,8 @@ describe Ci::Build do
it 'returns variables in order depending on resource hierarchy' do
is_expected.to eq(
- [build_pre_var,
+ [job_jwt_var,
+ build_pre_var,
project_pre_var,
pipeline_pre_var,
build_yaml_var,
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 80b619ed2b1..6f6ff3704b4 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -349,13 +349,16 @@ describe Ci::JobArtifact do
end
describe 'file is being stored' do
- context 'when object has nil store' do
- it 'is stored locally' do
- subject = build(:ci_job_artifact, :archive, file_store: nil)
+ subject { create(:ci_job_artifact, :archive) }
- subject.save
+ context 'when object has nil store' do
+ before do
+ subject.update_column(:file_store, nil)
+ subject.reload
+ end
- expect(subject.file_store).to be(ObjectStorage::Store::LOCAL)
+ it 'is stored locally' do
+ expect(subject.file_store).to be(nil)
expect(subject.file).to be_file_storage
expect(subject.file.object_store).to eq(ObjectStorage::Store::LOCAL)
end
@@ -363,10 +366,6 @@ describe Ci::JobArtifact do
context 'when existing object has local store' do
it 'is stored locally' do
- subject = build(:ci_job_artifact, :archive)
-
- subject.save
-
expect(subject.file_store).to be(ObjectStorage::Store::LOCAL)
expect(subject.file).to be_file_storage
expect(subject.file.object_store).to eq(ObjectStorage::Store::LOCAL)
@@ -380,10 +379,6 @@ describe Ci::JobArtifact do
context 'when file is stored' do
it 'is stored remotely' do
- subject = build(:ci_job_artifact, :archive)
-
- subject.save
-
expect(subject.file_store).to eq(ObjectStorage::Store::REMOTE)
expect(subject.file).not_to be_file_storage
expect(subject.file.object_store).to eq(ObjectStorage::Store::REMOTE)
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index e8ef7b29681..4490371bde5 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -6,6 +6,18 @@ describe Ci::Processable do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:detached_merge_request_pipeline) do
+ create(:ci_pipeline, :detached_merge_request_pipeline, :with_job, project: project)
+ end
+
+ let_it_be(:legacy_detached_merge_request_pipeline) do
+ create(:ci_pipeline, :legacy_detached_merge_request_pipeline, :with_job, project: project)
+ end
+
+ let_it_be(:merged_result_pipeline) do
+ create(:ci_pipeline, :merged_result_pipeline, :with_job, project: project)
+ end
+
describe '#aggregated_needs_names' do
let(:with_aggregated_needs) { pipeline.processables.select_with_aggregated_needs(project) }
@@ -155,4 +167,70 @@ describe Ci::Processable do
end
end
end
+
+ describe '#merge_request?' do
+ subject { pipeline.processables.first.merge_request? }
+
+ context 'in a detached merge request pipeline' do
+ let(:pipeline) { detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request?) }
+ end
+
+ context 'in a legacy detached merge_request_pipeline' do
+ let(:pipeline) { legacy_detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request?) }
+ end
+
+ context 'in a pipeline for merged results' do
+ let(:pipeline) { merged_result_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request?) }
+ end
+ end
+
+ describe '#merge_request_ref?' do
+ subject { pipeline.processables.first.merge_request_ref? }
+
+ context 'in a detached merge request pipeline' do
+ let(:pipeline) { detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request_ref?) }
+ end
+
+ context 'in a legacy detached merge_request_pipeline' do
+ let(:pipeline) { legacy_detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request_ref?) }
+ end
+
+ context 'in a pipeline for merged results' do
+ let(:pipeline) { merged_result_pipeline }
+
+ it { is_expected.to eq(pipeline.merge_request_ref?) }
+ end
+ end
+
+ describe '#legacy_detached_merge_request_pipeline?' do
+ subject { pipeline.processables.first.legacy_detached_merge_request_pipeline? }
+
+ context 'in a detached merge request pipeline' do
+ let(:pipeline) { detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
+ end
+
+ context 'in a legacy detached merge_request_pipeline' do
+ let(:pipeline) { legacy_detached_merge_request_pipeline }
+
+ it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
+ end
+
+ context 'in a pipeline for merged results' do
+ let(:pipeline) { merged_result_pipeline }
+
+ it { is_expected.to eq(pipeline.legacy_detached_merge_request_pipeline?) }
+ end
+ end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index b8034ba5bf2..2dedff7f15b 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -78,6 +78,36 @@ describe Ci::Runner do
.to raise_error(ActiveRecord::RecordInvalid)
end
end
+
+ context 'cost factors validations' do
+ it 'dissalows :private_projects_minutes_cost_factor being nil' do
+ runner = build(:ci_runner, private_projects_minutes_cost_factor: nil)
+
+ expect(runner).to be_invalid
+ expect(runner.errors.full_messages).to include('Private projects minutes cost factor needs to be non-negative')
+ end
+
+ it 'dissalows :public_projects_minutes_cost_factor being nil' do
+ runner = build(:ci_runner, public_projects_minutes_cost_factor: nil)
+
+ expect(runner).to be_invalid
+ expect(runner.errors.full_messages).to include('Public projects minutes cost factor needs to be non-negative')
+ end
+
+ it 'dissalows :private_projects_minutes_cost_factor being negative' do
+ runner = build(:ci_runner, private_projects_minutes_cost_factor: -1.1)
+
+ expect(runner).to be_invalid
+ expect(runner.errors.full_messages).to include('Private projects minutes cost factor needs to be non-negative')
+ end
+
+ it 'dissalows :public_projects_minutes_cost_factor being negative' do
+ runner = build(:ci_runner, public_projects_minutes_cost_factor: -2.2)
+
+ expect(runner).to be_invalid
+ expect(runner.errors.full_messages).to include('Public projects minutes cost factor needs to be non-negative')
+ end
+ end
end
describe 'constraints' do
diff --git a/spec/models/clusters/applications/fluentd_spec.rb b/spec/models/clusters/applications/fluentd_spec.rb
new file mode 100644
index 00000000000..7e9680b0ab4
--- /dev/null
+++ b/spec/models/clusters/applications/fluentd_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::Fluentd do
+ let(:fluentd) { create(:clusters_applications_fluentd) }
+
+ include_examples 'cluster application core specs', :clusters_applications_fluentd
+ include_examples 'cluster application status specs', :clusters_applications_fluentd
+ include_examples 'cluster application version specs', :clusters_applications_fluentd
+ include_examples 'cluster application initial status specs'
+
+ describe '#can_uninstall?' do
+ subject { fluentd.can_uninstall? }
+
+ it { is_expected.to be true }
+ end
+
+ describe '#install_command' do
+ subject { fluentd.install_command }
+
+ it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
+
+ it 'is initialized with fluentd arguments' do
+ expect(subject.name).to eq('fluentd')
+ expect(subject.chart).to eq('stable/fluentd')
+ expect(subject.version).to eq('2.4.0')
+ expect(subject).to be_rbac
+ end
+
+ context 'application failed to install previously' do
+ let(:fluentd) { create(:clusters_applications_fluentd, :errored, version: '0.0.1') }
+
+ it 'is initialized with the locked version' do
+ expect(subject.version).to eq('2.4.0')
+ end
+ end
+ end
+
+ describe '#files' do
+ let(:application) { fluentd }
+ let(:values) { subject[:'values.yaml'] }
+
+ subject { application.files }
+
+ it 'includes fluentd specific keys in the values.yaml file' do
+ expect(values).to include('output.conf', 'general.conf')
+ end
+ end
+end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 64d667f40f6..b070729ccac 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -219,6 +219,12 @@ describe Clusters::Applications::Ingress do
expect(subject.values).to include('extraContainers')
end
+
+ it 'includes livenessProbe for modsecurity sidecar container' do
+ probe_config = YAML.safe_load(subject.values).dig('controller', 'extraContainers', 0, 'livenessProbe')
+
+ expect(probe_config).to eq('exec' => { 'command' => ['ls', '/var/log/modsec/audit.log'] })
+ end
end
context 'when modsecurity_enabled is disabled' do
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 29c75186110..db1d8672d1e 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -582,9 +582,10 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
let!(:knative) { create(:clusters_applications_knative, cluster: cluster) }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: cluster) }
+ let!(:fluentd) { create(:clusters_applications_fluentd, cluster: cluster) }
it 'returns a list of created applications' do
- is_expected.to contain_exactly(helm, ingress, cert_manager, crossplane, prometheus, runner, jupyter, knative, elastic_stack)
+ is_expected.to contain_exactly(helm, ingress, cert_manager, crossplane, prometheus, runner, jupyter, knative, elastic_stack, fluentd)
end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index cc1bb164c16..24908785320 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -496,6 +496,40 @@ describe Issuable do
end
end
+ describe '.labels_hash' do
+ let(:feature_label) { create(:label, title: 'Feature') }
+ let(:second_label) { create(:label, title: 'Second Label') }
+ let!(:issues) { create_list(:labeled_issue, 3, labels: [feature_label, second_label]) }
+ let(:issue_id) { issues.first.id }
+
+ it 'maps issue ids to labels titles' do
+ expect(Issue.labels_hash[issue_id]).to include('Feature')
+ end
+
+ it 'works on relations filtered by multiple labels' do
+ relation = Issue.with_label(['Feature', 'Second Label'])
+
+ expect(relation.labels_hash[issue_id]).to include('Feature', 'Second Label')
+ end
+
+ # This tests the workaround for the lack of a NOT NULL constraint in
+ # label_links.label_id:
+ # https://gitlab.com/gitlab-org/gitlab/issues/197307
+ context 'with a NULL label ID in the link' do
+ let(:issue) { create(:labeled_issue, labels: [feature_label, second_label]) }
+
+ before do
+ label_link = issue.label_links.find_by(label_id: second_label.id)
+ label_link.label_id = nil
+ label_link.save(validate: false)
+ end
+
+ it 'filters out bad labels' do
+ expect(Issue.where(id: issue.id).labels_hash[issue.id]).to match_array(['Feature'])
+ end
+ end
+ end
+
describe '#user_notes_count' do
let(:project) { create(:project) }
let(:issue1) { create(:issue, project: project) }
diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb
index 1f410a7c539..ac169ebc0cf 100644
--- a/spec/models/cycle_analytics/group_level_spec.rb
+++ b/spec/models/cycle_analytics/group_level_spec.rb
@@ -38,7 +38,7 @@ describe CycleAnalytics::GroupLevel do
end
it 'returns medians for each stage for a specific group' do
- expect(subject.summary.map { |summary| summary[:value] }).to contain_exactly(1, 1)
+ expect(subject.summary.map { |summary| summary[:value] }).to contain_exactly('0.1', '1', '1')
end
end
end
diff --git a/spec/models/diff_note_position_spec.rb b/spec/models/diff_note_position_spec.rb
index dedb8a8da4d..d93e0af5526 100644
--- a/spec/models/diff_note_position_spec.rb
+++ b/spec/models/diff_note_position_spec.rb
@@ -40,4 +40,11 @@ describe DiffNotePosition, type: :model do
expect { diff_note_position.save! }.to raise_error(ActiveRecord::RecordNotUnique)
end
+
+ it 'accepts a line_range attribute' do
+ diff_note_position = build(:diff_note_position)
+
+ expect(diff_note_position).to respond_to(:line_range)
+ expect(diff_note_position).to respond_to(:line_range=)
+ end
end
diff --git a/spec/models/import_failure_spec.rb b/spec/models/import_failure_spec.rb
index d6574791a65..d286a4ad314 100644
--- a/spec/models/import_failure_spec.rb
+++ b/spec/models/import_failure_spec.rb
@@ -3,7 +3,28 @@
require 'spec_helper'
describe ImportFailure do
- describe "Associations" do
+ describe 'Scopes' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:correlation_id) { 'ABC' }
+ let_it_be(:hard_failure) { create(:import_failure, :hard_failure, project: project, correlation_id_value: correlation_id) }
+ let_it_be(:soft_failure) { create(:import_failure, :soft_failure, project: project, correlation_id_value: correlation_id) }
+ let_it_be(:unrelated_failure) { create(:import_failure, project: project) }
+
+ it 'returns hard failures given a correlation ID' do
+ expect(ImportFailure.hard_failures_by_correlation_id(correlation_id)).to eq([hard_failure])
+ end
+
+ it 'orders hard failures by newest first' do
+ older_failure = hard_failure.dup
+ Timecop.freeze(1.day.before(hard_failure.created_at)) do
+ older_failure.save!
+
+ expect(ImportFailure.hard_failures_by_correlation_id(correlation_id)).to eq([hard_failure, older_failure])
+ end
+ end
+ end
+
+ describe 'Associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:group) }
end
diff --git a/spec/models/jira_import_state_spec.rb b/spec/models/jira_import_state_spec.rb
index f75a17f71b2..4d91bf25b5e 100644
--- a/spec/models/jira_import_state_spec.rb
+++ b/spec/models/jira_import_state_spec.rb
@@ -130,8 +130,10 @@ describe JiraImportState do
context 'after transition to finished' do
let!(:jira_import) { build(:jira_import_state, :started, jid: 'some-other-jid', project: project)}
+ subject { jira_import.finish }
+
it 'triggers the import job' do
- jira_import.finish
+ subject
expect(jira_import.jid).to be_nil
end
@@ -139,11 +141,25 @@ describe JiraImportState do
it 'triggers the import job' do
jira_import.update!(status: :scheduled)
- jira_import.finish
+ subject
expect(jira_import.status).to eq('scheduled')
expect(jira_import.jid).to eq('some-other-jid')
end
+
+ it 'updates the record with imported issues counts' do
+ import_label = create(:label, project: project, title: 'jira-import')
+ create_list(:labeled_issue, 3, project: project, labels: [import_label])
+
+ expect(Gitlab::JiraImport).to receive(:get_import_label_id).and_return(import_label.id)
+ expect(Gitlab::JiraImport).to receive(:issue_failures).and_return(2)
+
+ subject
+
+ expect(jira_import.total_issue_count).to eq(5)
+ expect(jira_import.failed_to_import_count).to eq(2)
+ expect(jira_import.imported_issues_count).to eq(3)
+ end
end
end
end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 6d2ad3f0475..016af4f269b 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -566,6 +566,45 @@ describe MergeRequestDiff do
it 'returns affected file paths' do
expect(subject.modified_paths).to eq(%w{foo bar baz})
end
+
+ context "when fallback_on_overflow is true" do
+ let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
+ let(:diff) { merge_request.merge_request_diff }
+
+ # before do
+ # # Temporarily unstub diff.modified_paths in favor of original code
+ # #
+ # allow(diff).to receive(:modified_paths).and_call_original
+ # end
+
+ context "when the merge_request_diff is overflowed" do
+ before do
+ expect(diff).to receive(:overflow?).and_return(true)
+ end
+
+ it "returns file paths via project.repository#diff_stats" do
+ expect(diff.project.repository).to receive(:diff_stats).and_call_original
+
+ expect(
+ diff.modified_paths(fallback_on_overflow: true)
+ ).to eq(diff.modified_paths)
+ end
+ end
+
+ context "when the merge_request_diff is not overflowed" do
+ before do
+ expect(subject).to receive(:overflow?).and_return(false)
+ end
+
+ it "returns expect file paths withoout called #modified_paths_for_overflowed_mr" do
+ expect(subject.project.repository).not_to receive(:diff_stats)
+
+ expect(
+ subject.modified_paths(fallback_on_overflow: true)
+ ).to eq(subject.modified_paths)
+ end
+ end
+ end
end
describe '#opening_external_diff' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 50bb194ef71..52cd31ee65f 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -2335,6 +2335,21 @@ describe MergeRequest do
end
end
+ describe "#public_merge_status" do
+ using RSpec::Parameterized::TableSyntax
+ subject { build(:merge_request, merge_status: status) }
+
+ where(:status, :public_status) do
+ 'cannot_be_merged_rechecking' | 'checking'
+ 'checking' | 'checking'
+ 'cannot_be_merged' | 'cannot_be_merged'
+ end
+
+ with_them do
+ it { expect(subject.public_merge_status).to eq(public_status) }
+ end
+ end
+
describe "#head_pipeline_active? " do
it do
is_expected
@@ -3226,20 +3241,51 @@ describe MergeRequest do
expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once
expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
- subject.mark_as_unmergeable
- subject.mark_as_unchecked
- subject.mark_as_unmergeable
+ subject.mark_as_unmergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_unmergeable!
+ end
+
+ it 'notifies conflict, but does not notify again if rechecking still results in cannot_be_merged with async mergeability check' do
+ expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once
+ expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
+
+ subject.mark_as_checking!
+ subject.mark_as_unmergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_checking!
+ subject.mark_as_unmergeable!
end
it 'notifies conflict, whenever newly unmergeable' do
expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice
expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice
- subject.mark_as_unmergeable
- subject.mark_as_unchecked
- subject.mark_as_mergeable
- subject.mark_as_unchecked
- subject.mark_as_unmergeable
+ subject.mark_as_unmergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_mergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_unmergeable!
+ end
+
+ it 'notifies conflict, whenever newly unmergeable with async mergeability check' do
+ expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice
+ expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice
+
+ subject.mark_as_checking!
+ subject.mark_as_unmergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_checking!
+ subject.mark_as_mergeable!
+
+ subject.mark_as_unchecked!
+ subject.mark_as_checking!
+ subject.mark_as_unmergeable!
end
it 'does not notify whenever merge request is newly unmergeable due to other reasons' do
@@ -3248,7 +3294,7 @@ describe MergeRequest do
expect(notification_service).not_to receive(:merge_request_unmergeable)
expect(todo_service).not_to receive(:merge_request_became_unmergeable)
- subject.mark_as_unmergeable
+ subject.mark_as_unmergeable!
end
end
end
@@ -3261,7 +3307,7 @@ describe MergeRequest do
expect(notification_service).not_to receive(:merge_request_unmergeable)
expect(todo_service).not_to receive(:merge_request_became_unmergeable)
- subject.mark_as_unmergeable
+ subject.mark_as_unmergeable!
end
end
end
diff --git a/spec/models/metrics/dashboard/annotation_spec.rb b/spec/models/metrics/dashboard/annotation_spec.rb
index ed3bef37a7c..f7fd7ded7e6 100644
--- a/spec/models/metrics/dashboard/annotation_spec.rb
+++ b/spec/models/metrics/dashboard/annotation_spec.rb
@@ -50,4 +50,30 @@ describe Metrics::Dashboard::Annotation do
end
end
end
+
+ describe 'scopes' do
+ let_it_be(:nine_minutes_old_annotation) { create(:metrics_dashboard_annotation, starting_at: 9.minutes.ago) }
+ let_it_be(:fifteen_minutes_old_annotation) { create(:metrics_dashboard_annotation, starting_at: 15.minutes.ago) }
+ let_it_be(:just_created_annotation) { create(:metrics_dashboard_annotation) }
+
+ describe '#after' do
+ it 'returns only younger annotations' do
+ expect(described_class.after(12.minutes.ago)).to match_array [nine_minutes_old_annotation, just_created_annotation]
+ end
+ end
+
+ describe '#before' do
+ it 'returns only older annotations' do
+ expect(described_class.before(5.minutes.ago)).to match_array [fifteen_minutes_old_annotation, nine_minutes_old_annotation]
+ end
+ end
+
+ describe '#for_dashboard' do
+ let!(:other_dashboard_annotation) { create(:metrics_dashboard_annotation, dashboard_path: 'other_dashboard.yml') }
+
+ it 'returns annotations only for appointed dashboard' do
+ expect(described_class.for_dashboard('other_dashboard.yml')).to match_array [other_dashboard_annotation]
+ end
+ end
+ end
end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 6a333898955..38fba5ea071 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -27,7 +27,7 @@ describe ProjectFeature do
end
describe '#feature_available?' do
- let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
+ let(:features) { %w(issues wiki builds merge_requests snippets repository pages metrics_dashboard) }
context 'when features are disabled' do
it "returns false" do
@@ -123,7 +123,7 @@ describe ProjectFeature do
end
context 'public features' do
- features = %w(issues wiki builds merge_requests snippets repository)
+ features = %w(issues wiki builds merge_requests snippets repository metrics_dashboard)
features.each do |feature|
it "does not allow public access level for #{feature}" do
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index 720dc4f435f..cb34d898a6e 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -3,7 +3,10 @@
require 'spec_helper'
describe ProjectImportState, type: :model do
- subject { create(:import_state) }
+ let_it_be(:correlation_id) { 'cid' }
+ let_it_be(:import_state, refind: true) { create(:import_state, correlation_id_value: correlation_id) }
+
+ subject { import_state }
describe 'associations' do
it { is_expected.to belong_to(:project) }
@@ -33,12 +36,24 @@ describe ProjectImportState, type: :model do
end
it 'records job and correlation IDs', :sidekiq_might_not_need_inline do
- allow(Labkit::Correlation::CorrelationId).to receive(:current_or_new_id).and_return('abc')
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_or_new_id).and_return(correlation_id)
import_state.schedule
expect(import_state.jid).to be_an_instance_of(String)
- expect(import_state.correlation_id).to eq('abc')
+ expect(import_state.correlation_id).to eq(correlation_id)
+ end
+ end
+
+ describe '#relation_hard_failures' do
+ let_it_be(:failures) { create_list(:import_failure, 2, :hard_failure, project: import_state.project, correlation_id_value: correlation_id) }
+
+ it 'returns hard relation failures related to this import' do
+ expect(subject.relation_hard_failures(limit: 100)).to match_array(failures)
+ end
+
+ it 'limits returned collection to given maximum' do
+ expect(subject.relation_hard_failures(limit: 1).size).to eq(1)
end
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index 5565d30d8c1..a85dbe3a7df 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -418,4 +418,48 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end
end
end
+
+ describe '#editable?' do
+ it 'is editable' do
+ expect(service.editable?).to be(true)
+ end
+
+ context 'when cluster exists with prometheus installed' do
+ let(:cluster) { create(:cluster, projects: [project]) }
+
+ before do
+ service.update!(manual_configuration: false)
+
+ create(:clusters_applications_prometheus, :installed, cluster: cluster)
+ end
+
+ it 'remains editable' do
+ expect(service.editable?).to be(true)
+ end
+ end
+ end
+
+ describe '#fields' do
+ let(:expected_fields) do
+ [
+ {
+ type: 'checkbox',
+ name: 'manual_configuration',
+ title: s_('PrometheusService|Active'),
+ required: true
+ },
+ {
+ type: 'text',
+ name: 'api_url',
+ title: 'API URL',
+ placeholder: s_('PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/'),
+ required: true
+ }
+ ]
+ end
+
+ it 'returns fields' do
+ expect(service.fields).to eq(expected_fields)
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 3c8afee4466..4e75ef4fc87 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1740,32 +1740,12 @@ describe Project do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
- context 'when feature is enabled' do
- before do
- stub_feature_flags(project_search_by_full_path: true)
- end
-
- it 'returns projects that match the group path' do
- expect(described_class.search(group.path, include_namespace: true)).to eq([project])
- end
-
- it 'returns projects that match the full path' do
- expect(described_class.search(project.full_path, include_namespace: true)).to eq([project])
- end
+ it 'returns projects that match the group path' do
+ expect(described_class.search(group.path, include_namespace: true)).to eq([project])
end
- context 'when feature is disabled' do
- before do
- stub_feature_flags(project_search_by_full_path: false)
- end
-
- it 'returns no results when searching by group path' do
- expect(described_class.search(group.path, include_namespace: true)).to be_empty
- end
-
- it 'returns no results when searching by full path' do
- expect(described_class.search(project.full_path, include_namespace: true)).to be_empty
- end
+ it 'returns projects that match the full path' do
+ expect(described_class.search(project.full_path, include_namespace: true)).to eq([project])
end
end
@@ -2665,18 +2645,6 @@ describe Project do
end
end
- describe '#daily_statistics_enabled?' do
- it { is_expected.to be_daily_statistics_enabled }
-
- context 'when :project_daily_statistics is disabled for the project' do
- before do
- stub_feature_flags(project_daily_statistics: { thing: subject, enabled: false })
- end
-
- it { is_expected.not_to be_daily_statistics_enabled }
- end
- end
-
describe '#change_head' do
let(:project) { create(:project, :repository) }
diff --git a/spec/models/resource_milestone_event_spec.rb b/spec/models/resource_milestone_event_spec.rb
index 1b0181e3fd2..bf8672f95c9 100644
--- a/spec/models/resource_milestone_event_spec.rb
+++ b/spec/models/resource_milestone_event_spec.rb
@@ -52,4 +52,30 @@ describe ResourceMilestoneEvent, type: :model do
end
end
end
+
+ shared_examples 'a milestone action queryable resource event' do |expected_results_for_actions|
+ [Issue, MergeRequest].each do |klass|
+ expected_results_for_actions.each do |action, expected_result|
+ it "is #{expected_result} for action #{action} on #{klass.name.underscore}" do
+ model = create(klass.name.underscore)
+ key = model.class.name.underscore
+ event = build(described_class.name.underscore.to_sym, key => model, action: action)
+
+ expect(event.send(query_method)).to eq(expected_result)
+ end
+ end
+ end
+ end
+
+ describe '#added?' do
+ it_behaves_like 'a milestone action queryable resource event', { add: true, remove: false } do
+ let(:query_method) { :add? }
+ end
+ end
+
+ describe '#removed?' do
+ it_behaves_like 'a milestone action queryable resource event', { add: false, remove: true } do
+ let(:query_method) { :remove? }
+ end
+ end
end
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index 1d677e7ece5..3cd15e23ee2 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -5,24 +5,35 @@ require 'spec_helper'
describe Terraform::State do
subject { create(:terraform_state, :with_file) }
+ let(:terraform_state_file) { fixture_file('terraform/terraform.tfstate') }
+
it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:locked_by_user).class_name('User') }
+
it { is_expected.to validate_presence_of(:project_id) }
before do
stub_terraform_state_object_storage(Terraform::StateUploader)
end
- describe '#file_store' do
- context 'when no value is set' do
- it 'returns the default store of the uploader' do
- [ObjectStorage::Store::LOCAL, ObjectStorage::Store::REMOTE].each do |store|
- expect(Terraform::StateUploader).to receive(:default_store).and_return(store)
- expect(described_class.new.file_store).to eq(store)
- end
+ describe '#file' do
+ context 'when a file exists' do
+ it 'does not use the default file' do
+ expect(subject.file.read).to eq(terraform_state_file)
end
end
+ context 'when no file exists' do
+ subject { create(:terraform_state) }
+
+ it 'creates a default file' do
+ expect(subject.file.read).to eq('{"version":1}')
+ end
+ end
+ end
+
+ describe '#file_store' do
context 'when a value is set' do
it 'returns the value' do
[ObjectStorage::Store::LOCAL, ObjectStorage::Store::REMOTE].each do |store|
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 5a3e16baa87..8597397c3c6 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -4357,18 +4357,19 @@ describe User, :do_not_mock_admin_mode do
describe 'internal methods' do
let_it_be(:user) { create(:user) }
- let!(:ghost) { described_class.ghost }
- let!(:alert_bot) { described_class.alert_bot }
- let!(:non_internal) { [user] }
- let!(:internal) { [ghost, alert_bot] }
+ let_it_be(:ghost) { described_class.ghost }
+ let_it_be(:alert_bot) { described_class.alert_bot }
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+ let_it_be(:non_internal) { [user, project_bot] }
+ let_it_be(:internal) { [ghost, alert_bot] }
it 'returns internal users' do
- expect(described_class.internal).to eq(internal)
+ expect(described_class.internal).to match_array(internal)
expect(internal.all?(&:internal?)).to eq(true)
end
it 'returns non internal users' do
- expect(described_class.non_internal).to eq(non_internal)
+ expect(described_class.non_internal).to match_array(non_internal)
expect(non_internal.all?(&:internal?)).to eq(false)
end
@@ -4420,9 +4421,12 @@ describe User, :do_not_mock_admin_mode do
it 'returns corresponding users' do
human = create(:user)
bot = create(:user, :bot)
+ project_bot = create(:user, :project_bot)
expect(described_class.humans).to match_array([human])
- expect(described_class.bots).to match_array([bot])
+ expect(described_class.bots).to match_array([bot, project_bot])
+ expect(described_class.bots_without_project_bot).to match_array([bot])
+ expect(described_class.with_project_bots).to match_array([human, project_bot])
end
end
@@ -4655,4 +4659,30 @@ describe User, :do_not_mock_admin_mode do
it { is_expected.to be :locked }
end
end
+
+ describe '#password_required?' do
+ let_it_be(:user) { create(:user) }
+
+ shared_examples 'does not require password to be present' do
+ it { expect(user).not_to validate_presence_of(:password) }
+
+ it { expect(user).not_to validate_presence_of(:password_confirmation) }
+ end
+
+ context 'when user is an internal user' do
+ before do
+ user.update(user_type: 'alert_bot')
+ end
+
+ it_behaves_like 'does not require password to be present'
+ end
+
+ context 'when user is a project bot user' do
+ before do
+ user.update(user_type: 'project_bot')
+ end
+
+ it_behaves_like 'does not require password to be present'
+ end
+ end
end
diff --git a/spec/models/user_type_enums_spec.rb b/spec/models/user_type_enums_spec.rb
new file mode 100644
index 00000000000..4f56e6ea96e
--- /dev/null
+++ b/spec/models/user_type_enums_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe UserTypeEnums do
+ it '.types' do
+ expect(described_class.types.keys).to include('alert_bot', 'project_bot', 'human', 'ghost')
+ end
+
+ it '.bots' do
+ expect(described_class.bots.keys).to include('alert_bot', 'project_bot')
+ end
+end