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/ci/build_spec.rb52
-rw-r--r--spec/models/ci/pipeline_spec.rb55
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb10
-rw-r--r--spec/models/commit_spec.rb84
-rw-r--r--spec/models/commit_status_spec.rb6
-rw-r--r--spec/models/concerns/issuable_spec.rb13
-rw-r--r--spec/models/concerns/redis_cacheable_spec.rb83
-rw-r--r--spec/models/concerns/sortable_spec.rb108
-rw-r--r--spec/models/generic_commit_status_spec.rb6
-rw-r--r--spec/models/repository_spec.rb14
10 files changed, 411 insertions, 20 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index dc810489011..96173889ccd 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -629,6 +629,14 @@ describe Ci::Build do
it { is_expected.to eq('review/host') }
end
+
+ context 'when using persisted variables' do
+ let(:build) do
+ create(:ci_build, environment: 'review/x$CI_BUILD_ID')
+ end
+
+ it { is_expected.to eq('review/x') }
+ end
end
describe '#starts_environment?' do
@@ -1270,6 +1278,46 @@ describe Ci::Build do
end
end
+ describe '#playable?' do
+ context 'when build is a manual action' do
+ context 'when build has been skipped' do
+ subject { build_stubbed(:ci_build, :manual, status: :skipped) }
+
+ it { is_expected.not_to be_playable }
+ end
+
+ context 'when build has been canceled' do
+ subject { build_stubbed(:ci_build, :manual, status: :canceled) }
+
+ it { is_expected.to be_playable }
+ end
+
+ context 'when build is successful' do
+ subject { build_stubbed(:ci_build, :manual, status: :success) }
+
+ it { is_expected.to be_playable }
+ end
+
+ context 'when build has failed' do
+ subject { build_stubbed(:ci_build, :manual, status: :failed) }
+
+ it { is_expected.to be_playable }
+ end
+
+ context 'when build is a manual untriggered action' do
+ subject { build_stubbed(:ci_build, :manual, status: :manual) }
+
+ it { is_expected.to be_playable }
+ end
+ end
+
+ context 'when build is not a manual action' do
+ subject { build_stubbed(:ci_build, :success) }
+
+ it { is_expected.not_to be_playable }
+ end
+ end
+
describe 'project settings' do
describe '#allow_git_fetch' do
it 'return project allow_git_fetch configuration' do
@@ -1485,6 +1533,7 @@ describe Ci::Build do
let(:container_registry_enabled) { false }
let(:predefined_variables) do
[
+ { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
{ key: 'CI_JOB_ID', value: build.id.to_s, public: true },
{ key: 'CI_JOB_TOKEN', value: build.token, public: false },
{ key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
@@ -1516,7 +1565,6 @@ describe Ci::Build do
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true },
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
- { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
{ key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
{ key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true },
@@ -2044,7 +2092,7 @@ describe Ci::Build do
let(:deploy_token_variables) do
[
- { key: 'CI_DEPLOY_USER', value: deploy_token.name, public: true },
+ { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true },
{ key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false }
]
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index ddd66a6be87..e4f4c62bd22 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -167,13 +167,39 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#persisted_variables' do
+ context 'when pipeline is not persisted yet' do
+ subject { build(:ci_pipeline).persisted_variables }
+
+ it 'does not contain some variables' do
+ keys = subject.map { |variable| variable[:key] }
+
+ expect(keys).not_to include 'CI_PIPELINE_ID'
+ end
+ end
+
+ context 'when pipeline is persisted' do
+ subject { build_stubbed(:ci_pipeline).persisted_variables }
+
+ it 'does contains persisted variables' do
+ keys = subject.map { |variable| variable[:key] }
+
+ expect(keys).to eq %w[CI_PIPELINE_ID]
+ end
+ end
+ end
+
describe '#predefined_variables' do
subject { pipeline.predefined_variables }
it 'includes all predefined variables in a valid order' do
keys = subject.map { |variable| variable[:key] }
- expect(keys).to eq %w[CI_PIPELINE_ID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION]
+ expect(keys).to eq %w[CI_CONFIG_PATH
+ CI_PIPELINE_SOURCE
+ CI_COMMIT_MESSAGE
+ CI_COMMIT_TITLE
+ CI_COMMIT_DESCRIPTION]
end
end
@@ -774,6 +800,33 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#number_of_warnings' do
+ it 'returns the number of warnings' do
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop')
+
+ expect(pipeline.number_of_warnings).to eq(1)
+ end
+
+ it 'supports eager loading of the number of warnings' do
+ pipeline2 = create(:ci_empty_pipeline, status: :created, project: project)
+
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop')
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline2, name: 'rubocop')
+
+ pipelines = project.pipelines.to_a
+
+ pipelines.each(&:number_of_warnings)
+
+ # To run the queries we need to actually use the lazy objects, which we do
+ # by just sending "to_i" to them.
+ amount = ActiveRecord::QueryRecorder
+ .new { pipelines.each { |p| p.number_of_warnings.to_i } }
+ .count
+
+ expect(amount).to eq(1)
+ end
+ end
+
shared_context 'with some outdated pipelines' do
before do
create_pipeline(:canceled, 'ref', 'A', project)
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index aeca6ee903a..407e2fc598a 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -85,6 +85,16 @@ describe Clusters::Applications::Prometheus do
it 'copies options and headers from kube client to proxy client' do
expect(subject.prometheus_client.options).to eq(kube_client.rest_client.options.merge(headers: kube_client.headers))
end
+
+ context 'when cluster is not reachable' do
+ before do
+ allow(kube_client).to receive(:proxy_url).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil))
+ end
+
+ it 'returns nil' do
+ expect(subject.prometheus_client).to be_nil
+ end
+ end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 448b813c2e1..090f91168ad 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -52,22 +52,98 @@ describe Commit do
end
end
- describe '#author' do
+ describe '#author', :request_store do
it 'looks up the author in a case-insensitive way' do
user = create(:user, email: commit.author_email.upcase)
expect(commit.author).to eq(user)
end
- it 'caches the author', :request_store do
+ it 'caches the author' do
user = create(:user, email: commit.author_email)
- expect(User).to receive(:find_by_any_email).and_call_original
expect(commit.author).to eq(user)
+
key = "Commit:author:#{commit.author_email.downcase}"
- expect(RequestStore.store[key]).to eq(user)
+ expect(RequestStore.store[key]).to eq(user)
expect(commit.author).to eq(user)
end
+
+ context 'using eager loading' do
+ let!(:alice) { create(:user, email: 'alice@example.com') }
+ let!(:bob) { create(:user, email: 'hunter2@example.com') }
+
+ let(:alice_commit) do
+ described_class.new(RepoHelpers.sample_commit, project).tap do |c|
+ c.author_email = 'alice@example.com'
+ end
+ end
+
+ let(:bob_commit) do
+ # The commit for Bob uses one of his alternative Emails, instead of the
+ # primary one.
+ described_class.new(RepoHelpers.sample_commit, project).tap do |c|
+ c.author_email = 'bob@example.com'
+ end
+ end
+
+ let(:eve_commit) do
+ described_class.new(RepoHelpers.sample_commit, project).tap do |c|
+ c.author_email = 'eve@example.com'
+ end
+ end
+
+ let!(:commits) { [alice_commit, bob_commit, eve_commit] }
+
+ before do
+ create(:email, user: bob, email: 'bob@example.com')
+ end
+
+ it 'executes only two SQL queries' do
+ recorder = ActiveRecord::QueryRecorder.new do
+ # Running this first ensures we don't run one query for every
+ # commit.
+ commits.each(&:lazy_author)
+
+ # This forces the execution of the SQL queries necessary to load the
+ # data.
+ commits.each { |c| c.author.try(:id) }
+ end
+
+ expect(recorder.count).to eq(2)
+ end
+
+ it "preloads the authors for Commits matching a user's primary Email" do
+ commits.each(&:lazy_author)
+
+ expect(alice_commit.author).to eq(alice)
+ end
+
+ it "preloads the authors for Commits using a User's alternative Email" do
+ commits.each(&:lazy_author)
+
+ expect(bob_commit.author).to eq(bob)
+ end
+
+ it 'sets the author to Nil if an author could not be found for a Commit' do
+ commits.each(&:lazy_author)
+
+ expect(eve_commit.author).to be_nil
+ end
+
+ it 'does not execute SQL queries once the authors are preloaded' do
+ commits.each(&:lazy_author)
+ commits.each { |c| c.author.try(:id) }
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ alice_commit.author
+ bob_commit.author
+ eve_commit.author
+ end
+
+ expect(recorder.count).to be_zero
+ end
+ end
end
describe '#to_reference' do
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 2ed29052dc1..f3f2bc28d2c 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -565,4 +565,10 @@ describe CommitStatus do
it_behaves_like 'commit status enqueued'
end
end
+
+ describe '#present' do
+ subject { commit_status.present }
+
+ it { is_expected.to be_a(CommitStatusPresenter) }
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 3d3092b8ac9..bd6bf5b0712 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -266,6 +266,19 @@ describe Issuable do
end
end
+ describe '#time_estimate=' do
+ it 'coerces the value below Gitlab::Database::MAX_INT_VALUE' do
+ expect { issue.time_estimate = 100 }.to change { issue.time_estimate }.to(100)
+ expect { issue.time_estimate = Gitlab::Database::MAX_INT_VALUE + 100 }.to change { issue.time_estimate }.to(Gitlab::Database::MAX_INT_VALUE)
+ end
+
+ it 'skips coercion for not Integer values' do
+ expect { issue.time_estimate = nil }.to change { issue.time_estimate }.to(nil)
+ expect { issue.time_estimate = 'invalid time' }.not_to raise_error(StandardError)
+ expect { issue.time_estimate = 22.33 }.not_to raise_error(StandardError)
+ end
+ end
+
describe '#to_hook_data' do
let(:builder) { double }
diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb
index 3d7963120b6..23c6c6233e9 100644
--- a/spec/models/concerns/redis_cacheable_spec.rb
+++ b/spec/models/concerns/redis_cacheable_spec.rb
@@ -1,21 +1,36 @@
require 'spec_helper'
describe RedisCacheable do
- let(:model) { double }
+ let(:model) do
+ Struct.new(:id, :attributes) do
+ def read_attribute(attribute)
+ attributes[attribute]
+ end
+
+ def cast_value_from_cache(attribute, cached_value)
+ cached_value
+ end
+
+ def has_attribute?(attribute)
+ attributes.has_key?(attribute)
+ end
+ end
+ end
+
+ let(:payload) { { name: 'value', time: Time.zone.now } }
+ let(:instance) { model.new(1, payload) }
+ let(:cache_key) { instance.__send__(:cache_attribute_key) }
before do
- model.extend(described_class)
- allow(model).to receive(:cache_attribute_key).and_return('key')
+ model.include(described_class)
end
describe '#cached_attribute' do
- let(:payload) { { attribute: 'value' } }
-
- subject { model.cached_attribute(payload.keys.first) }
+ subject { instance.cached_attribute(payload.keys.first) }
it 'gets the cache attribute' do
Gitlab::Redis::SharedState.with do |redis|
- expect(redis).to receive(:get).with('key')
+ expect(redis).to receive(:get).with(cache_key)
.and_return(payload.to_json)
end
@@ -24,16 +39,62 @@ describe RedisCacheable do
end
describe '#cache_attributes' do
- let(:values) { { name: 'new_name' } }
-
- subject { model.cache_attributes(values) }
+ subject { instance.cache_attributes(payload) }
it 'sets the cache attributes' do
Gitlab::Redis::SharedState.with do |redis|
- expect(redis).to receive(:set).with('key', values.to_json, anything)
+ expect(redis).to receive(:set).with(cache_key, payload.to_json, anything)
end
subject
end
end
+
+ describe '#cached_attr_reader', :clean_gitlab_redis_shared_state do
+ subject { instance.name }
+
+ before do
+ model.cached_attr_reader(:name)
+ end
+
+ context 'when there is no cached value' do
+ it 'reads the attribute' do
+ expect(instance).to receive(:read_attribute).and_call_original
+
+ expect(subject).to eq(payload[:name])
+ end
+ end
+
+ context 'when there is a cached value' do
+ it 'reads the cached value' do
+ expect(instance).not_to receive(:read_attribute)
+
+ instance.cache_attributes(payload)
+
+ expect(subject).to eq(payload[:name])
+ end
+ end
+
+ it 'always returns the latest values' do
+ expect(instance.name).to eq(payload[:name])
+
+ instance.cache_attributes(name: 'new_value')
+
+ expect(instance.name).to eq('new_value')
+ end
+ end
+
+ describe '#cast_value_from_cache' do
+ subject { instance.__send__(:cast_value_from_cache, attribute, value) }
+
+ context 'with runner contacted_at' do
+ let(:instance) { Ci::Runner.new }
+ let(:attribute) { :contacted_at }
+ let(:value) { '2018-05-07 13:53:08 UTC' }
+
+ it 'converts cache string to appropriate type' do
+ expect(subject).to be_an_instance_of(ActiveSupport::TimeWithZone)
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb
new file mode 100644
index 00000000000..b821a84d5e0
--- /dev/null
+++ b/spec/models/concerns/sortable_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+
+describe Sortable do
+ describe '.order_by' do
+ let(:relation) { Group.all }
+
+ describe 'ordering by id' do
+ it 'ascending' do
+ expect(relation).to receive(:reorder).with(id: :asc)
+
+ relation.order_by('id_asc')
+ end
+
+ it 'descending' do
+ expect(relation).to receive(:reorder).with(id: :desc)
+
+ relation.order_by('id_desc')
+ end
+ end
+
+ describe 'ordering by created day' do
+ it 'ascending' do
+ expect(relation).to receive(:reorder).with(created_at: :asc)
+
+ relation.order_by('created_asc')
+ end
+
+ it 'descending' do
+ expect(relation).to receive(:reorder).with(created_at: :desc)
+
+ relation.order_by('created_desc')
+ end
+
+ it 'order by "date"' do
+ expect(relation).to receive(:reorder).with(created_at: :desc)
+
+ relation.order_by('created_date')
+ end
+ end
+
+ describe 'ordering by name' do
+ it 'ascending' do
+ expect(relation).to receive(:reorder).with("lower(name) asc")
+
+ relation.order_by('name_asc')
+ end
+
+ it 'descending' do
+ expect(relation).to receive(:reorder).with("lower(name) desc")
+
+ relation.order_by('name_desc')
+ end
+ end
+
+ describe 'ordering by Updated Time' do
+ it 'ascending' do
+ expect(relation).to receive(:reorder).with(updated_at: :asc)
+
+ relation.order_by('updated_asc')
+ end
+
+ it 'descending' do
+ expect(relation).to receive(:reorder).with(updated_at: :desc)
+
+ relation.order_by('updated_desc')
+ end
+ end
+
+ it 'does not call reorder in case of unrecognized ordering' do
+ expect(relation).not_to receive(:reorder)
+
+ relation.order_by('random_ordering')
+ end
+ end
+
+ describe 'sorting groups' do
+ def ordered_group_names(order)
+ Group.all.order_by(order).map(&:name)
+ end
+
+ let!(:ref_time) { Time.parse('2018-05-01 00:00:00') }
+ let!(:group1) { create(:group, name: 'aa', id: 1, created_at: ref_time - 15.seconds, updated_at: ref_time) }
+ let!(:group2) { create(:group, name: 'AAA', id: 2, created_at: ref_time - 10.seconds, updated_at: ref_time - 5.seconds) }
+ let!(:group3) { create(:group, name: 'BB', id: 3, created_at: ref_time - 5.seconds, updated_at: ref_time - 10.seconds) }
+ let!(:group4) { create(:group, name: 'bbb', id: 4, created_at: ref_time, updated_at: ref_time - 15.seconds) }
+
+ it 'sorts groups by id' do
+ expect(ordered_group_names('id_asc')).to eq(%w(aa AAA BB bbb))
+ expect(ordered_group_names('id_desc')).to eq(%w(bbb BB AAA aa))
+ end
+
+ it 'sorts groups by name via case-insentitive comparision' do
+ expect(ordered_group_names('name_asc')).to eq(%w(aa AAA BB bbb))
+ expect(ordered_group_names('name_desc')).to eq(%w(bbb BB AAA aa))
+ end
+
+ it 'sorts groups by created_at' do
+ expect(ordered_group_names('created_asc')).to eq(%w(aa AAA BB bbb))
+ expect(ordered_group_names('created_desc')).to eq(%w(bbb BB AAA aa))
+ expect(ordered_group_names('created_date')).to eq(%w(bbb BB AAA aa))
+ end
+
+ it 'sorts groups by updated_at' do
+ expect(ordered_group_names('updated_asc')).to eq(%w(bbb BB AAA aa))
+ expect(ordered_group_names('updated_desc')).to eq(%w(aa AAA BB bbb))
+ end
+ end
+end
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index 673049d1cc4..a3e68d2e646 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -78,4 +78,10 @@ describe GenericCommitStatus do
it { is_expected.not_to be_nil }
end
end
+
+ describe '#present' do
+ subject { generic_commit_status.present }
+
+ it { is_expected.to be_a(GenericCommitStatusPresenter) }
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index ac8d9a32d4e..6bc148a1392 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -671,7 +671,7 @@ describe Repository do
end
end
- describe "search_files_by_content" do
+ shared_examples "search_files_by_content" do
let(:results) { repository.search_files_by_content('feature', 'master') }
subject { results }
@@ -718,7 +718,7 @@ describe Repository do
end
end
- describe "search_files_by_name" do
+ shared_examples "search_files_by_name" do
let(:results) { repository.search_files_by_name('files', 'master') }
it 'returns result' do
@@ -758,6 +758,16 @@ describe Repository do
end
end
+ describe 'with gitaly enabled' do
+ it_behaves_like 'search_files_by_content'
+ it_behaves_like 'search_files_by_name'
+ end
+
+ describe 'with gitaly disabled', :disable_gitaly do
+ it_behaves_like 'search_files_by_content'
+ it_behaves_like 'search_files_by_name'
+ end
+
describe '#async_remove_remote' do
before do
masterrev = repository.find_branch('master').dereferenced_target