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/models')
-rw-r--r--spec/models/analytics/devops_adoption/segment_selection_spec.rb4
-rw-r--r--spec/models/bulk_imports/entity_spec.rb64
-rw-r--r--spec/models/ci/build_spec.rb54
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb46
-rw-r--r--spec/models/ci/job_artifact_spec.rb16
-rw-r--r--spec/models/ci/pipeline_spec.rb43
-rw-r--r--spec/models/clusters/applications/helm_spec.rb40
-rw-r--r--spec/models/deployment_spec.rb70
-rw-r--r--spec/models/diff_note_spec.rb20
-rw-r--r--spec/models/environment_spec.rb27
-rw-r--r--spec/models/exported_protected_branch_spec.rb21
-rw-r--r--spec/models/instance_configuration_spec.rb16
-rw-r--r--spec/models/issue_spec.rb19
-rw-r--r--spec/models/merge_request_reviewer_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb33
-rw-r--r--spec/models/namespace_onboarding_action_spec.rb7
-rw-r--r--spec/models/namespace_spec.rb1
-rw-r--r--spec/models/pages/lookup_path_spec.rb59
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/models/snippet_spec.rb12
-rw-r--r--spec/models/system_note_metadata_spec.rb12
-rw-r--r--spec/models/terraform/state_spec.rb113
-rw-r--r--spec/models/user_spec.rb133
23 files changed, 578 insertions, 235 deletions
diff --git a/spec/models/analytics/devops_adoption/segment_selection_spec.rb b/spec/models/analytics/devops_adoption/segment_selection_spec.rb
index 5866cbaa48e..64ef6636f2c 100644
--- a/spec/models/analytics/devops_adoption/segment_selection_spec.rb
+++ b/spec/models/analytics/devops_adoption/segment_selection_spec.rb
@@ -51,11 +51,13 @@ RSpec.describe Analytics::DevopsAdoption::SegmentSelection, type: :model do
context 'limit the number of segment selections' do
let_it_be(:segment) { create(:devops_adoption_segment) }
- subject { build(:devops_adoption_segment_selection, segment: segment, project: project) }
+ subject { build(:devops_adoption_segment_selection, project: project, segment: segment) }
before do
create(:devops_adoption_segment_selection, :project, segment: segment)
+ segment.reload
+
stub_const("#{described_class}::ALLOWED_SELECTIONS_PER_SEGMENT", 1)
end
diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb
index ad6e3ec6f30..0732b671729 100644
--- a/spec/models/bulk_imports/entity_spec.rb
+++ b/spec/models/bulk_imports/entity_spec.rb
@@ -82,4 +82,68 @@ RSpec.describe BulkImports::Entity, type: :model do
end
end
end
+
+ describe "#update_tracker_for" do
+ let(:entity) { create(:bulk_import_entity) }
+
+ it "inserts new tracker when it does not exist" do
+ expect do
+ entity.update_tracker_for(relation: :relation, has_next_page: false)
+ end.to change(BulkImports::Tracker, :count).by(1)
+
+ tracker = entity.trackers.last
+
+ expect(tracker.relation).to eq('relation')
+ expect(tracker.has_next_page).to eq(false)
+ expect(tracker.next_page).to eq(nil)
+ end
+
+ it "updates the tracker if it already exist" do
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ entity: entity
+ )
+
+ expect do
+ entity.update_tracker_for(relation: :relation, has_next_page: true, next_page: 'nextPage')
+ end.not_to change(BulkImports::Tracker, :count)
+
+ tracker = entity.trackers.last
+
+ expect(tracker.relation).to eq('relation')
+ expect(tracker.has_next_page).to eq(true)
+ expect(tracker.next_page).to eq('nextPage')
+ end
+ end
+
+ describe "#has_next_page?" do
+ it "queries for the given relation if it has more pages to be fetched" do
+ entity = create(:bulk_import_entity)
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ entity: entity
+ )
+
+ expect(entity.has_next_page?(:relation)).to eq(false)
+ end
+ end
+
+ describe "#next_page_for" do
+ it "queries for the next page of the given relation" do
+ entity = create(:bulk_import_entity)
+ create(
+ :bulk_import_tracker,
+ relation: :relation,
+ has_next_page: false,
+ next_page: 'nextPage',
+ entity: entity
+ )
+
+ expect(entity.next_page_for(:relation)).to eq('nextPage')
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 5ff9b4dd493..0efb014fdfc 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1151,12 +1151,26 @@ RSpec.describe Ci::Build do
end
context 'when transits to skipped' do
- before do
- build.skip!
+ context 'when cd_skipped_deployment_status is disabled' do
+ before do
+ stub_feature_flags(cd_skipped_deployment_status: false)
+ build.skip!
+ end
+
+ it 'transits deployment status to canceled' do
+ expect(deployment).to be_canceled
+ end
end
- it 'transits deployment status to canceled' do
- expect(deployment).to be_canceled
+ context 'when cd_skipped_deployment_status is enabled' do
+ before do
+ stub_feature_flags(cd_skipped_deployment_status: project)
+ build.skip!
+ end
+
+ it 'transits deployment status to skipped' do
+ expect(deployment).to be_skipped
+ end
end
end
@@ -4057,6 +4071,38 @@ RSpec.describe Ci::Build do
end
end
+ describe '#collect_codequality_reports!' do
+ subject(:codequality_report) { build.collect_codequality_reports!(Gitlab::Ci::Reports::CodequalityReports.new) }
+
+ it { expect(codequality_report.degradations).to eq({}) }
+
+ context 'when build has a codequality report' do
+ context 'when there is a codequality report' do
+ before do
+ create(:ci_job_artifact, :codequality, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the codequality report' do
+ expect { codequality_report }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(3)
+ end
+ end
+
+ context 'when there is an codequality report without errors' do
+ before do
+ create(:ci_job_artifact, :codequality_without_errors, job: build, project: build.project)
+ end
+
+ it 'parses blobs and add the results to the codequality report' do
+ expect { codequality_report }.not_to raise_error
+
+ expect(codequality_report.degradations_count).to eq(0)
+ end
+ end
+ end
+ end
+
describe '#collect_terraform_reports!' do
let(:terraform_reports) { Gitlab::Ci::Reports::TerraformReports.new }
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index 20ca0c8b710..bc96e2584cf 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -74,6 +74,52 @@ RSpec.describe Ci::BuildTraceChunks::Fog do
expect(data_store.data(model)).to eq new_data
end
+
+ context 'when S3 server side encryption is enabled' do
+ before do
+ config = Gitlab.config.artifacts.object_store.to_h
+ config[:storage_options] = { server_side_encryption: 'AES256' }
+ allow(data_store).to receive(:object_store_raw_config).and_return(config)
+ end
+
+ it 'creates a file with attributes' do
+ expect_next_instance_of(Fog::AWS::Storage::Files) do |files|
+ expect(files).to receive(:create).with(
+ hash_including(
+ key: anything,
+ body: new_data,
+ 'x-amz-server-side-encryption' => 'AES256')
+ ).and_call_original
+ end
+
+ expect(data_store.data(model)).to be_nil
+
+ data_store.set_data(model, new_data)
+
+ expect(data_store.data(model)).to eq new_data
+ end
+
+ context 'when ci_live_trace_use_fog_attributes flag is disabled' do
+ before do
+ stub_feature_flags(ci_live_trace_use_fog_attributes: false)
+ end
+
+ it 'does not pass along Fog attributes' do
+ expect_next_instance_of(Fog::AWS::Storage::Files) do |files|
+ expect(files).to receive(:create).with(
+ key: anything,
+ body: new_data
+ ).and_call_original
+ end
+
+ expect(data_store.data(model)).to be_nil
+
+ data_store.set_data(model, new_data)
+
+ expect(data_store.data(model)).to eq new_data
+ end
+ end
+ end
end
end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 26851c93ac3..ef21ca8f100 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -96,6 +96,22 @@ RSpec.describe Ci::JobArtifact do
end
end
+ describe '.codequality_reports' do
+ subject { described_class.codequality_reports }
+
+ context 'when there is a codequality report' do
+ let!(:artifact) { create(:ci_job_artifact, :codequality) }
+
+ it { is_expected.to eq([artifact]) }
+ end
+
+ context 'when there are no codequality reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
describe '.terraform_reports' do
context 'when there is a terraform report' do
it 'return the job artifact' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 1ca370dc950..5a29e7b5c6c 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -498,6 +498,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ context 'when pipeline has a codequality report' do
+ subject { described_class.with_reports(Ci::JobArtifact.codequality_reports) }
+
+ let(:pipeline_with_report) { create(:ci_pipeline, :with_codequality_reports) }
+
+ it 'selects the pipeline' do
+ is_expected.to eq([pipeline_with_report])
+ end
+ end
+
context 'when pipeline has a terraform report' do
it 'selects the pipeline' do
pipeline_with_report = create(:ci_pipeline, :with_terraform_reports)
@@ -3360,6 +3370,39 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '#codequality_reports' do
+ subject(:codequality_reports) { pipeline.codequality_reports }
+
+ context 'when pipeline has multiple builds with codequality reports' do
+ let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ before do
+ create(:ci_job_artifact, :codequality, job: build_rspec, project: project)
+ create(:ci_job_artifact, :codequality_without_errors, job: build_golang, project: project)
+ end
+
+ it 'returns codequality report with collected data' do
+ expect(codequality_reports.degradations_count).to eq(3)
+ end
+
+ context 'when builds are retried' do
+ let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
+ let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+
+ it 'returns a codequality reports without degradations' do
+ expect(codequality_reports.degradations).to be_empty
+ end
+ end
+ end
+
+ context 'when pipeline does not have any builds with codequality reports' do
+ it 'returns codequality reports without degradations' do
+ expect(codequality_reports.degradations).to be_empty
+ end
+ end
+ end
+
describe '#total_size' do
let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let!(:build_job2) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index ad1ebd4966a..5212e321a55 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -19,35 +19,9 @@ RSpec.describe Clusters::Applications::Helm do
end
describe '#can_uninstall?' do
- context "with other existing applications" do
- Clusters::Cluster::APPLICATIONS.keys.each do |application_name|
- next if application_name == 'helm'
+ subject(:application) { build(:clusters_applications_helm).can_uninstall? }
- it "is false when #{application_name} is installed" do
- cluster_application = create("clusters_applications_#{application_name}".to_sym)
-
- helm = cluster_application.cluster.application_helm
-
- expect(helm.allowed_to_uninstall?).to be_falsy
- end
- end
-
- it 'executes a single query only' do
- cluster_application = create(:clusters_applications_ingress)
- helm = cluster_application.cluster.application_helm
-
- query_count = ActiveRecord::QueryRecorder.new { helm.allowed_to_uninstall? }.count
- expect(query_count).to eq(1)
- end
- end
-
- context "without other existing applications" do
- subject { helm.can_uninstall? }
-
- let(:helm) { create(:clusters_applications_helm) }
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to eq true }
end
describe '#issue_client_cert' do
@@ -135,14 +109,4 @@ RSpec.describe Clusters::Applications::Helm do
end
end
end
-
- describe '#post_uninstall' do
- let(:helm) { create(:clusters_applications_helm, :installed) }
-
- it do
- expect(helm.cluster.kubeclient).to receive(:delete_namespace).with('gitlab-managed-apps')
-
- helm.post_uninstall
- end
- end
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 9afacd518af..3da1509f075 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -202,6 +202,31 @@ RSpec.describe Deployment do
deployment.cancel!
end
end
+
+ context 'when deployment was skipped' do
+ let(:deployment) { create(:deployment, :running) }
+
+ it 'has correct status' do
+ deployment.skip!
+
+ expect(deployment).to be_skipped
+ expect(deployment.finished_at).to be_nil
+ end
+
+ it 'does not execute Deployments::LinkMergeRequestWorker asynchronously' do
+ expect(Deployments::LinkMergeRequestWorker)
+ .not_to receive(:perform_async).with(deployment.id)
+
+ deployment.skip!
+ end
+
+ it 'does not execute Deployments::ExecuteHooksWorker' do
+ expect(Deployments::ExecuteHooksWorker)
+ .not_to receive(:perform_async).with(deployment.id)
+
+ deployment.skip!
+ end
+ end
end
describe '#success?' do
@@ -320,6 +345,7 @@ RSpec.describe Deployment do
deployment2 = create(:deployment, status: :running )
create(:deployment, status: :failed )
create(:deployment, status: :canceled )
+ create(:deployment, status: :skipped)
is_expected.to contain_exactly(deployment1, deployment2)
end
@@ -346,10 +372,54 @@ RSpec.describe Deployment do
it 'retrieves deployments with deployable builds' do
with_deployable = create(:deployment)
create(:deployment, deployable: nil)
+ create(:deployment, deployable_type: 'CommitStatus', deployable_id: non_existing_record_id)
is_expected.to contain_exactly(with_deployable)
end
end
+
+ describe 'visible' do
+ subject { described_class.visible }
+
+ it 'retrieves the visible deployments' do
+ deployment1 = create(:deployment, status: :running)
+ deployment2 = create(:deployment, status: :success)
+ deployment3 = create(:deployment, status: :failed)
+ deployment4 = create(:deployment, status: :canceled)
+ create(:deployment, status: :skipped)
+
+ is_expected.to contain_exactly(deployment1, deployment2, deployment3, deployment4)
+ end
+ end
+ end
+
+ describe 'latest_for_sha' do
+ subject { described_class.latest_for_sha(sha) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:commits) { project.repository.commits('master', limit: 2) }
+ let_it_be(:deployments) { commits.reverse.map { |commit| create(:deployment, project: project, sha: commit.id) } }
+ let(:sha) { commits.map(&:id) }
+
+ it 'finds the latest deployment with sha' do
+ is_expected.to eq(deployments.last)
+ end
+
+ context 'when sha is old' do
+ let(:sha) { commits.last.id }
+
+ it 'finds the latest deployment with sha' do
+ is_expected.to eq(deployments.first)
+ end
+ end
+
+ context 'when sha is nil' do
+ let(:sha) { nil }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
end
describe '#includes_commit?' do
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index f7ce44f7281..215c733f26b 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -38,6 +38,26 @@ RSpec.describe DiffNote do
it_behaves_like 'a valid diff positionable note' do
subject { build(:diff_note_on_commit, project: project, commit_id: commit_id, position: position) }
end
+
+ it "is not valid when noteable is empty" do
+ note = build(:diff_note_on_merge_request, project: project, noteable: nil)
+
+ note.valid?
+
+ expect(note.errors[:noteable]).to include("doesn't support new-style diff notes")
+ end
+
+ context 'when importing' do
+ it "does not check if it's supported" do
+ note = build(:diff_note_on_merge_request, project: project, noteable: nil)
+ note.importing = true
+ note.valid?
+
+ expect(note.errors.full_messages).not_to include(
+ "Noteable doesn't support new-style diff notes"
+ )
+ end
+ end
end
describe "#position=" do
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 179f2a1b0e0..27c9e62712c 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -1394,4 +1394,31 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to be(false) }
end
end
+
+ describe '#cancel_deployment_jobs!' do
+ subject { environment.cancel_deployment_jobs! }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:environment, reload: true) { create(:environment, project: project) }
+ let!(:deployment) { create(:deployment, project: project, environment: environment, deployable: build) }
+ let!(:build) { create(:ci_build, :running, project: project, environment: environment) }
+
+ it 'cancels an active deployment job' do
+ subject
+
+ expect(build.reset).to be_canceled
+ end
+
+ context 'when deployable does not exist' do
+ before do
+ deployment.update_column(:deployable_id, non_existing_record_id)
+ end
+
+ it 'does not raise an error' do
+ expect { subject }.not_to raise_error
+
+ expect(build.reset).to be_running
+ end
+ end
+ end
end
diff --git a/spec/models/exported_protected_branch_spec.rb b/spec/models/exported_protected_branch_spec.rb
new file mode 100644
index 00000000000..7886a522741
--- /dev/null
+++ b/spec/models/exported_protected_branch_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ExportedProtectedBranch do
+ describe 'Associations' do
+ it { is_expected.to have_many(:push_access_levels) }
+ end
+
+ describe '.push_access_levels' do
+ it 'returns the correct push access levels' do
+ exported_branch = create(:exported_protected_branch, :developers_can_push)
+ deploy_key = create(:deploy_key)
+ create(:deploy_keys_project, :write_access, project: exported_branch.project, deploy_key: deploy_key )
+ create(:protected_branch_push_access_level, protected_branch: exported_branch, deploy_key: deploy_key)
+ dev_push_access_level = exported_branch.push_access_levels.first
+
+ expect(exported_branch.push_access_levels).to contain_exactly(dev_push_access_level)
+ end
+ end
+end
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 383e548c324..d3566ed04c2 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -10,31 +10,35 @@ RSpec.describe InstanceConfiguration do
let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do
- stub_pub_file(exist: false)
+ stub_pub_file(pub_file(exist: false))
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end
it 'does not return anything if file is empty' do
- stub_pub_file
+ stub_pub_file(pub_file)
- allow(File).to receive(:read).and_return('')
+ stub_file_read(pub_file, content: '')
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end
it 'returns the md5 and sha256 if file valid and exists' do
- stub_pub_file
+ stub_pub_file(pub_file)
result = subject.settings[:ssh_algorithms_hashes].select { |o| o[:md5] == md5 && o[:sha256] == sha256 }
expect(result.size).to eq(InstanceConfiguration::SSH_ALGORITHMS.size)
end
- def stub_pub_file(exist: true)
+ def pub_file(exist: true)
path = exist ? 'spec/fixtures/ssh_host_example_key.pub' : 'spec/fixtures/ssh_host_example_key.pub.random'
- allow(subject).to receive(:ssh_algorithm_file).and_return(Rails.root.join(path))
+ Rails.root.join(path)
+ end
+
+ def stub_pub_file(path)
+ allow(subject).to receive(:ssh_algorithm_file).and_return(path)
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 16ea2989eda..81f045b4db1 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -141,7 +141,6 @@ RSpec.describe Issue do
describe '.with_issue_type' do
let_it_be(:issue) { create(:issue, project: reusable_project) }
let_it_be(:incident) { create(:incident, project: reusable_project) }
- let_it_be(:test_case) { create(:quality_test_case, project: reusable_project) }
it 'gives issues with the given issue type' do
expect(described_class.with_issue_type('issue'))
@@ -149,8 +148,8 @@ RSpec.describe Issue do
end
it 'gives issues with the given issue type' do
- expect(described_class.with_issue_type(%w(issue incident test_case)))
- .to contain_exactly(issue, incident, test_case)
+ expect(described_class.with_issue_type(%w(issue incident)))
+ .to contain_exactly(issue, incident)
end
end
@@ -370,6 +369,20 @@ RSpec.describe Issue do
expect(link_types).not_to include(nil)
end
+ it 'returns issues including the link creation time' do
+ dates = authorized_issue_a.related_issues(user).map(&:issue_link_created_at)
+
+ expect(dates).not_to be_empty
+ expect(dates).not_to include(nil)
+ end
+
+ it 'returns issues including the link update time' do
+ dates = authorized_issue_a.related_issues(user).map(&:issue_link_updated_at)
+
+ expect(dates).not_to be_empty
+ expect(dates).not_to include(nil)
+ end
+
describe 'when a user cannot read cross project' do
it 'only returns issues within the same project' do
expect(Ability).to receive(:allowed?).with(user, :read_all_resources, :global).at_least(:once).and_call_original
diff --git a/spec/models/merge_request_reviewer_spec.rb b/spec/models/merge_request_reviewer_spec.rb
index 55199cd96ad..76b44abca54 100644
--- a/spec/models/merge_request_reviewer_spec.rb
+++ b/spec/models/merge_request_reviewer_spec.rb
@@ -9,6 +9,6 @@ RSpec.describe MergeRequestReviewer do
describe 'associations' do
it { is_expected.to belong_to(:merge_request).class_name('MergeRequest') }
- it { is_expected.to belong_to(:reviewer).class_name('User') }
+ it { is_expected.to belong_to(:reviewer).class_name('User').inverse_of(:merge_request_reviewers) }
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 9574c57e46c..454c3e1ba7f 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -92,6 +92,39 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.not_to include(mr_without_jira_reference) }
end
+ context 'scopes' do
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+
+ let_it_be(:merge_request1) { create(:merge_request, :unique_branches, reviewers: [user1])}
+ let_it_be(:merge_request2) { create(:merge_request, :unique_branches, reviewers: [user2])}
+ let_it_be(:merge_request3) { create(:merge_request, :unique_branches, reviewers: [])}
+
+ describe '.review_requested' do
+ it 'returns MRs that has any review requests' do
+ expect(described_class.review_requested).to eq([merge_request1, merge_request2])
+ end
+ end
+
+ describe '.no_review_requested' do
+ it 'returns MRs that has no review requests' do
+ expect(described_class.no_review_requested).to eq([merge_request3])
+ end
+ end
+
+ describe '.review_requested_to' do
+ it 'returns MRs that the user has been requested to review' do
+ expect(described_class.review_requested_to(user1)).to eq([merge_request1])
+ end
+ end
+
+ describe '.no_review_requested_to' do
+ it 'returns MRs that the user has been requested to review' do
+ expect(described_class.no_review_requested_to(user1)).to eq([merge_request2, merge_request3])
+ end
+ end
+ end
+
describe '#squash_in_progress?' do
let(:repo_path) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
diff --git a/spec/models/namespace_onboarding_action_spec.rb b/spec/models/namespace_onboarding_action_spec.rb
new file mode 100644
index 00000000000..9dd82c05032
--- /dev/null
+++ b/spec/models/namespace_onboarding_action_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe NamespaceOnboardingAction do
+ it { is_expected.to belong_to :namespace }
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 85f9005052e..0130618d004 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe Namespace do
it { is_expected.to have_one :aggregation_schedule }
it { is_expected.to have_one :namespace_settings }
it { is_expected.to have_many :custom_emoji }
+ it { is_expected.to have_many :namespace_onboarding_actions }
end
describe 'validations' do
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index f8ebc237577..632313f26d3 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe Pages::LookupPath do
before do
stub_pages_setting(access_control: true, external_https: ["1.1.1.1:443"])
- stub_artifacts_object_storage
stub_pages_object_storage(::Pages::DeploymentUploader)
end
@@ -117,64 +116,6 @@ RSpec.describe Pages::LookupPath do
include_examples 'uses disk storage'
end
end
-
- context 'when artifact_id from build job is present in pages metadata' do
- let(:artifacts_archive) { create(:ci_job_artifact, :zip, :remote_store, project: project) }
-
- before do
- project.mark_pages_as_deployed(artifacts_archive: artifacts_archive)
- end
-
- it 'uses artifacts object storage' do
- Timecop.freeze do
- expect(source).to(
- eq({
- type: 'zip',
- path: artifacts_archive.file.url(expire_at: 1.day.from_now),
- global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
- sha256: artifacts_archive.file_sha256,
- file_size: artifacts_archive.size,
- file_count: nil
- })
- )
- end
- end
-
- context 'when artifact is not uploaded to object storage' do
- let(:artifacts_archive) { create(:ci_job_artifact, :zip) }
-
- it 'uses file protocol', :aggregate_failures do
- Timecop.freeze do
- expect(source).to(
- eq({
- type: 'zip',
- path: 'file://' + artifacts_archive.file.path,
- global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
- sha256: artifacts_archive.file_sha256,
- file_size: artifacts_archive.size,
- file_count: nil
- })
- )
- end
- end
-
- context 'when pages_serve_with_zip_file_protocol feature flag is disabled' do
- before do
- stub_feature_flags(pages_serve_with_zip_file_protocol: false)
- end
-
- include_examples 'uses disk storage'
- end
- end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(pages_serve_from_artifacts_archive: false)
- end
-
- include_examples 'uses disk storage'
- end
- end
end
describe '#prefix' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c8b96963d5d..b4da57ed89b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:deploy_keys) }
it { is_expected.to have_many(:hooks) }
it { is_expected.to have_many(:protected_branches) }
+ it { is_expected.to have_many(:exported_protected_branches) }
it { is_expected.to have_one(:slack_service) }
it { is_expected.to have_one(:microsoft_teams_service) }
it { is_expected.to have_one(:mattermost_service) }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index d74f5faab7f..9cf22a9392d 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -481,17 +481,9 @@ RSpec.describe Snippet do
end
describe '#blobs' do
- let(:snippet) { create(:snippet) }
-
- it 'returns a blob representing the snippet data' do
- blob = snippet.blob
-
- expect(blob).to be_a(Blob)
- expect(blob.path).to eq(snippet.file_name)
- expect(blob.data).to eq(snippet.content)
- end
-
context 'when repository does not exist' do
+ let(:snippet) { create(:snippet) }
+
it 'returns empty array' do
expect(snippet.blobs).to be_empty
end
diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb
index 9a6b57afb97..144c65d2f62 100644
--- a/spec/models/system_note_metadata_spec.rb
+++ b/spec/models/system_note_metadata_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe SystemNoteMetadata do
context 'when action type is invalid' do
subject do
- build(:system_note_metadata, note: build(:note), action: 'invalid_type' )
+ build(:system_note_metadata, note: build(:note), action: 'invalid_type')
end
it { is_expected.to be_invalid }
@@ -21,7 +21,15 @@ RSpec.describe SystemNoteMetadata do
context 'when action type is valid' do
subject do
- build(:system_note_metadata, note: build(:note), action: 'merge' )
+ build(:system_note_metadata, note: build(:note), action: 'merge')
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when importing' do
+ subject do
+ build(:system_note_metadata, note: nil, action: 'merge', importing: true)
end
it { is_expected.to be_valid }
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index ca8fe4cca3b..ef91e4a5a71 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -3,21 +3,13 @@
require 'spec_helper'
RSpec.describe Terraform::State do
- subject { create(:terraform_state, :with_file) }
-
- let(:terraform_state_file) { fixture_file('terraform/terraform.tfstate') }
-
- it { is_expected.to be_a FileStoreMounter }
+ subject { create(:terraform_state, :with_version) }
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
- end
-
describe 'scopes' do
describe '.ordered_by_name' do
let_it_be(:project) { create(:project) }
@@ -35,64 +27,18 @@ RSpec.describe Terraform::State do
end
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
- 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|
- expect(build(:terraform_state, file_store: store).file_store).to eq(store)
- end
- end
- end
- end
-
- describe '#update_file_store' do
- context 'when file is stored in object storage' do
- it_behaves_like 'mounted file in object store'
- end
-
- context 'when file is stored locally' do
- before do
- stub_terraform_state_object_storage(enabled: false)
- end
-
- it_behaves_like 'mounted file in local store'
- end
- end
-
describe '#latest_file' do
- subject { terraform_state.latest_file }
-
- context 'versioning is enabled' do
- let(:terraform_state) { create(:terraform_state, :with_version) }
- let(:latest_version) { terraform_state.latest_version }
-
- it { is_expected.to eq latest_version.file }
-
- context 'but no version exists yet' do
- let(:terraform_state) { create(:terraform_state) }
+ let(:terraform_state) { create(:terraform_state, :with_version) }
+ let(:latest_version) { terraform_state.latest_version }
- it { is_expected.to be_nil }
- end
- end
-
- context 'versioning is disabled' do
- let(:terraform_state) { create(:terraform_state, :with_file) }
+ subject { terraform_state.latest_file }
- it { is_expected.to eq terraform_state.file }
+ it { is_expected.to eq latest_version.file }
- context 'and a version exists (migration to versioned in progress)' do
- let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state) }
+ context 'but no version exists yet' do
+ let(:terraform_state) { create(:terraform_state) }
- it { is_expected.to eq terraform_state.latest_version.file }
- end
+ it { is_expected.to be_nil }
end
end
@@ -115,39 +61,30 @@ RSpec.describe Terraform::State do
end
end
- context 'versioning is disabled' do
- let(:terraform_state) { create(:terraform_state, :with_file) }
-
- it 'modifies the existing state record' do
- expect { subject }.not_to change { Terraform::StateVersion.count }
+ context 'versioning is disabled (migration to versioned in progress)' do
+ let(:terraform_state) { create(:terraform_state, versioning_enabled: false) }
+ let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state, version: 0) }
- expect(terraform_state.latest_file.read).to eq(data)
- end
-
- context 'and a version exists (migration to versioned in progress)' do
- let!(:migrated_version) { create(:terraform_state_version, terraform_state: terraform_state, version: 0) }
+ it 'creates a new version, corrects the migrated version number, and marks the state as versioned' do
+ expect { subject }.to change { Terraform::StateVersion.count }
- it 'creates a new version, corrects the migrated version number, and marks the state as versioned' do
- expect { subject }.to change { Terraform::StateVersion.count }
+ expect(migrated_version.reload.version).to eq(1)
+ expect(migrated_version.file.read).to eq(fixture_file('terraform/terraform.tfstate'))
- expect(migrated_version.reload.version).to eq(1)
- expect(migrated_version.file.read).to eq(terraform_state_file)
+ expect(terraform_state.reload.latest_version.version).to eq(version)
+ expect(terraform_state.latest_version.file.read).to eq(data)
+ expect(terraform_state).to be_versioning_enabled
+ end
- expect(terraform_state.reload.latest_version.version).to eq(version)
- expect(terraform_state.latest_version.file.read).to eq(data)
- expect(terraform_state).to be_versioning_enabled
+ context 'the current version cannot be determined' do
+ before do
+ migrated_version.update!(file: CarrierWaveStringFile.new('invalid-json'))
end
- context 'the current version cannot be determined' do
- before do
- migrated_version.update!(file: CarrierWaveStringFile.new('invalid-json'))
- end
-
- it 'uses version - 1 to correct the migrated version number' do
- expect { subject }.to change { Terraform::StateVersion.count }
+ it 'uses version - 1 to correct the migrated version number' do
+ expect { subject }.to change { Terraform::StateVersion.count }
- expect(migrated_version.reload.version).to eq(2)
- end
+ expect(migrated_version.reload.version).to eq(2)
end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 19a6a3ce3c4..87dab84fe7b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -99,6 +99,8 @@ RSpec.describe User do
it { is_expected.to have_many(:releases).dependent(:nullify) }
it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:user) }
it { is_expected.to have_many(:reviews).inverse_of(:author) }
+ it { is_expected.to have_many(:merge_request_assignees).inverse_of(:assignee) }
+ it { is_expected.to have_many(:merge_request_reviewers).inverse_of(:reviewer) }
describe "#user_detail" do
it 'does not persist `user_detail` by default' do
@@ -2024,9 +2026,10 @@ RSpec.describe User do
end
describe '.search' do
- let!(:user) { create(:user, name: 'user', username: 'usern', email: 'email@gmail.com') }
- let!(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@gmail.com') }
- let!(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@gmail.com') }
+ let_it_be(:user) { create(:user, name: 'user', username: 'usern', email: 'email@example.com') }
+ let_it_be(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@example.com') }
+ let_it_be(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@example.com') }
+ let_it_be(:email) { create(:email, user: user, email: 'alias@example.com') }
describe 'name matching' do
it 'returns users with a matching name with exact match first' do
@@ -2056,7 +2059,7 @@ RSpec.describe User do
end
it 'does not return users with a partially matching Email' do
- expect(described_class.search(user.email[0..2])).not_to include(user, user2)
+ expect(described_class.search(user.email[1...-1])).to be_empty
end
it 'returns users with a matching Email regardless of the casing' do
@@ -2064,6 +2067,36 @@ RSpec.describe User do
end
end
+ describe 'secondary email matching' do
+ context 'feature flag :user_search_secondary_email is enabled' do
+ it 'returns users with a matching secondary email' do
+ expect(described_class.search(email.email)).to include(email.user)
+ end
+
+ it 'does not return users with a matching part of secondary email' do
+ expect(described_class.search(email.email[1...-1])).to be_empty
+ end
+
+ it 'returns users with a matching secondary email regardless of the casing' do
+ expect(described_class.search(email.email.upcase)).to include(email.user)
+ end
+ end
+
+ context 'feature flag :user_search_secondary_email is disabled' do
+ before do
+ stub_feature_flags(user_search_secondary_email: false)
+ end
+
+ it 'does not return users with a matching secondary email' do
+ expect(described_class.search(email.email)).not_to include(email.user)
+ end
+
+ it 'does not return users with a matching part of secondary email' do
+ expect(described_class.search(email.email[1...-1])).to be_empty
+ end
+ end
+ end
+
describe 'username matching' do
it 'returns users with a matching username' do
expect(described_class.search(user.username)).to eq([user, user2])
@@ -2103,65 +2136,119 @@ RSpec.describe User do
end
end
- describe '.search_with_secondary_emails' do
- delegate :search_with_secondary_emails, to: :described_class
+ describe '.search_without_secondary_emails' do
+ let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
+ let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
+ let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
+
+ it 'returns users with a matching name' do
+ expect(described_class.search_without_secondary_emails(user.name)).to eq([user])
+ end
+
+ it 'returns users with a partially matching name' do
+ expect(described_class.search_without_secondary_emails(user.name[0..2])).to eq([user])
+ end
+
+ it 'returns users with a matching name regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.name.upcase)).to eq([user])
+ end
+
+ it 'returns users with a matching email' do
+ expect(described_class.search_without_secondary_emails(user.email)).to eq([user])
+ end
+
+ it 'does not return users with a partially matching email' do
+ expect(described_class.search_without_secondary_emails(user.email[1...-1])).to be_empty
+ end
+
+ it 'returns users with a matching email regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.email.upcase)).to eq([user])
+ end
+
+ it 'returns users with a matching username' do
+ expect(described_class.search_without_secondary_emails(user.username)).to eq([user])
+ end
+
+ it 'returns users with a partially matching username' do
+ expect(described_class.search_without_secondary_emails(user.username[0..2])).to eq([user])
+ end
+
+ it 'returns users with a matching username regardless of the casing' do
+ expect(described_class.search_without_secondary_emails(user.username.upcase)).to eq([user])
+ end
+
+ it 'does not return users with a matching whole secondary email' do
+ expect(described_class.search_without_secondary_emails(email.email)).not_to include(email.user)
+ end
+
+ it 'does not return users with a matching part of secondary email' do
+ expect(described_class.search_without_secondary_emails(email.email[1...-1])).to be_empty
+ end
- let!(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'john.doe@example.com' ) }
- let!(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'albert.smith@example.com' ) }
- let!(:email) do
- create(:email, user: another_user, email: 'alias@example.com')
+ it 'returns no matches for an empty string' do
+ expect(described_class.search_without_secondary_emails('')).to be_empty
+ end
+
+ it 'returns no matches for nil' do
+ expect(described_class.search_without_secondary_emails(nil)).to be_empty
end
+ end
+
+ describe '.search_with_secondary_emails' do
+ let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
+ let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
+ let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
it 'returns users with a matching name' do
- expect(search_with_secondary_emails(user.name)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name)).to eq([user])
end
it 'returns users with a partially matching name' do
- expect(search_with_secondary_emails(user.name[0..2])).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name[0..2])).to eq([user])
end
it 'returns users with a matching name regardless of the casing' do
- expect(search_with_secondary_emails(user.name.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.name.upcase)).to eq([user])
end
it 'returns users with a matching email' do
- expect(search_with_secondary_emails(user.email)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.email)).to eq([user])
end
it 'does not return users with a partially matching email' do
- expect(search_with_secondary_emails(user.email[0..2])).not_to include([user])
+ expect(described_class.search_with_secondary_emails(user.email[1...-1])).to be_empty
end
it 'returns users with a matching email regardless of the casing' do
- expect(search_with_secondary_emails(user.email.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.email.upcase)).to eq([user])
end
it 'returns users with a matching username' do
- expect(search_with_secondary_emails(user.username)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username)).to eq([user])
end
it 'returns users with a partially matching username' do
- expect(search_with_secondary_emails(user.username[0..2])).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username[0..2])).to eq([user])
end
it 'returns users with a matching username regardless of the casing' do
- expect(search_with_secondary_emails(user.username.upcase)).to eq([user])
+ expect(described_class.search_with_secondary_emails(user.username.upcase)).to eq([user])
end
it 'returns users with a matching whole secondary email' do
- expect(search_with_secondary_emails(email.email)).to eq([email.user])
+ expect(described_class.search_with_secondary_emails(email.email)).to eq([email.user])
end
it 'does not return users with a matching part of secondary email' do
- expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
+ expect(described_class.search_with_secondary_emails(email.email[1...-1])).to be_empty
end
it 'returns no matches for an empty string' do
- expect(search_with_secondary_emails('')).to be_empty
+ expect(described_class.search_with_secondary_emails('')).to be_empty
end
it 'returns no matches for nil' do
- expect(search_with_secondary_emails(nil)).to be_empty
+ expect(described_class.search_with_secondary_emails(nil)).to be_empty
end
end