summaryrefslogtreecommitdiff
path: root/spec/models/concerns
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
commita7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch)
tree7452bd5c3545c2fa67a28aa013835fb4fa071baf /spec/models/concerns
parentee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff)
downloadgitlab-ce-a7b3560714b4d9cc4ab32dffcd1f74a284b93580.tar.gz
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'spec/models/concerns')
-rw-r--r--spec/models/concerns/after_commit_queue_spec.rb4
-rw-r--r--spec/models/concerns/ci/has_variable_spec.rb39
-rw-r--r--spec/models/concerns/cross_database_modification_spec.rb89
-rw-r--r--spec/models/concerns/has_environment_scope_spec.rb32
-rw-r--r--spec/models/concerns/issuable_spec.rb8
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb10
-rw-r--r--spec/models/concerns/taskable_spec.rb66
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb138
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb10
9 files changed, 392 insertions, 4 deletions
diff --git a/spec/models/concerns/after_commit_queue_spec.rb b/spec/models/concerns/after_commit_queue_spec.rb
index 40cddde333e..8f091081dce 100644
--- a/spec/models/concerns/after_commit_queue_spec.rb
+++ b/spec/models/concerns/after_commit_queue_spec.rb
@@ -75,7 +75,7 @@ RSpec.describe AfterCommitQueue do
skip_if_multiple_databases_not_setup
table_sql = <<~SQL
- CREATE TABLE _test_ci_after_commit_queue (
+ CREATE TABLE _test_gitlab_ci_after_commit_queue (
id serial NOT NULL PRIMARY KEY);
SQL
@@ -84,7 +84,7 @@ RSpec.describe AfterCommitQueue do
let(:ci_klass) do
Class.new(Ci::ApplicationRecord) do
- self.table_name = '_test_ci_after_commit_queue'
+ self.table_name = '_test_gitlab_ci_after_commit_queue'
include AfterCommitQueue
diff --git a/spec/models/concerns/ci/has_variable_spec.rb b/spec/models/concerns/ci/has_variable_spec.rb
index e917ec6b802..bf699119a37 100644
--- a/spec/models/concerns/ci/has_variable_spec.rb
+++ b/spec/models/concerns/ci/has_variable_spec.rb
@@ -68,9 +68,48 @@ RSpec.describe Ci::HasVariable do
end
describe '#to_runner_variable' do
+ let_it_be(:ci_variable) { create(:ci_variable) }
+
+ subject { ci_variable }
+
it 'returns a hash for the runner' do
expect(subject.to_runner_variable)
.to include(key: subject.key, value: subject.value, public: false)
end
+
+ context 'with RequestStore enabled', :request_store do
+ let(:expected) do
+ {
+ file: false,
+ key: subject.key,
+ value: subject.value,
+ public: false,
+ masked: false
+ }
+ end
+
+ it 'decrypts once' do
+ expect(OpenSSL::PKCS5).to receive(:pbkdf2_hmac).once.and_call_original
+
+ 2.times { expect(subject.reload.to_runner_variable).to eq(expected) }
+ end
+
+ it 'does not cache similar keys', :aggregate_failures do
+ group_var = create(:ci_group_variable, key: subject.key, value: 'group')
+ project_var = create(:ci_variable, key: subject.key, value: 'project')
+
+ expect(subject.to_runner_variable).to include(key: subject.key, value: subject.value)
+ expect(group_var.to_runner_variable).to include(key: subject.key, value: 'group')
+ expect(project_var.to_runner_variable).to include(key: subject.key, value: 'project')
+ end
+
+ it 'does not cache unpersisted values' do
+ new_variable = Ci::Variable.new(key: SecureRandom.hex, value: "12345")
+ old_value = new_variable.to_runner_variable
+ new_variable.value = '98765'
+
+ expect(new_variable.to_runner_variable).not_to eq(old_value)
+ end
+ end
end
end
diff --git a/spec/models/concerns/cross_database_modification_spec.rb b/spec/models/concerns/cross_database_modification_spec.rb
new file mode 100644
index 00000000000..72544536953
--- /dev/null
+++ b/spec/models/concerns/cross_database_modification_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CrossDatabaseModification do
+ describe '.transaction' do
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(track_gitlab_schema_in_current_transaction: false)
+ end
+
+ it 'does not add to gitlab_transactions_stack' do
+ ApplicationRecord.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+ end
+ end
+
+ context 'feature flag is not yet setup' do
+ before do
+ allow(Feature::FlipperFeature).to receive(:table_exists?).and_raise(ActiveRecord::NoDatabaseError)
+ end
+
+ it 'does not add to gitlab_transactions_stack' do
+ ApplicationRecord.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+ end
+ end
+
+ it 'adds the current gitlab schema to gitlab_transactions_stack', :aggregate_failures do
+ ApplicationRecord.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_main)
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ Ci::ApplicationRecord.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_ci)
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ Project.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_main)
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ Ci::Pipeline.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_ci)
+
+ Project.first
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+
+ ApplicationRecord.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_main)
+
+ Ci::Pipeline.transaction do
+ expect(ApplicationRecord.gitlab_transactions_stack).to contain_exactly(:gitlab_main, :gitlab_ci)
+
+ Project.first
+ end
+ end
+
+ expect(ApplicationRecord.gitlab_transactions_stack).to be_empty
+ end
+
+ it 'yields' do
+ expect { |block| ApplicationRecord.transaction(&block) }.to yield_control
+ end
+ end
+end
diff --git a/spec/models/concerns/has_environment_scope_spec.rb b/spec/models/concerns/has_environment_scope_spec.rb
index 0cc997709c9..6e8394b6fa5 100644
--- a/spec/models/concerns/has_environment_scope_spec.rb
+++ b/spec/models/concerns/has_environment_scope_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe HasEnvironmentScope do
+ let_it_be(:project) { create(:project) }
+
subject { build(:ci_variable) }
it { is_expected.to allow_value('*').for(:environment_scope) }
@@ -17,8 +19,6 @@ RSpec.describe HasEnvironmentScope do
end
describe '.on_environment' do
- let(:project) { create(:project) }
-
it 'returns scoped objects' do
variable1 = create(:ci_variable, project: project, environment_scope: '*')
variable2 = create(:ci_variable, project: project, environment_scope: 'product/*')
@@ -63,4 +63,32 @@ RSpec.describe HasEnvironmentScope do
end
end
end
+
+ describe '.for_environment' do
+ subject { project.variables.for_environment(environment) }
+
+ let_it_be(:variable1) do
+ create(:ci_variable, project: project, environment_scope: '*')
+ end
+
+ let_it_be(:variable2) do
+ create(:ci_variable, project: project, environment_scope: 'production/*')
+ end
+
+ let_it_be(:variable3) do
+ create(:ci_variable, project: project, environment_scope: 'staging/*')
+ end
+
+ context 'when the environment is present' do
+ let(:environment) { 'production/canary-1' }
+
+ it { is_expected.to eq([variable1, variable2]) }
+ end
+
+ context 'when the environment is nil' do
+ let(:environment) {}
+
+ it { is_expected.to eq([variable1]) }
+ end
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index e9c3d1dc646..832d5b44e5d 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -935,6 +935,14 @@ RSpec.describe Issuable do
subject { issuable.supports_escalation? }
it { is_expected.to eq(supports_escalation) }
+
+ context 'with feature disabled' do
+ before do
+ stub_feature_flags(incident_escalations: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
end
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index fc154738f11..7e08f47fb5a 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -584,4 +584,14 @@ RSpec.describe Discussion, ResolvableDiscussion do
expect(subject.last_resolved_note).to eq(second_note)
end
end
+
+ describe '#clear_memoized_values' do
+ it 'resets the memoized values' do
+ described_class.memoized_values.each do |memo|
+ subject.instance_variable_set("@#{memo}", 'memoized')
+ expect { subject.clear_memoized_values }.to change { subject.instance_variable_get("@#{memo}") }
+ .from('memoized').to(nil)
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/taskable_spec.rb b/spec/models/concerns/taskable_spec.rb
new file mode 100644
index 00000000000..6b41174a046
--- /dev/null
+++ b/spec/models/concerns/taskable_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Taskable do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '.get_tasks' do
+ let(:description) do
+ <<~MARKDOWN
+ Any text before the list
+ - [ ] First item
+ - [x] Second item
+ * [x] First item
+ * [ ] Second item
+ MARKDOWN
+ end
+
+ let(:expected_result) do
+ [
+ TaskList::Item.new('- [ ]', 'First item'),
+ TaskList::Item.new('- [x]', 'Second item'),
+ TaskList::Item.new('* [x]', 'First item'),
+ TaskList::Item.new('* [ ]', 'Second item')
+ ]
+ end
+
+ subject { described_class.get_tasks(description) }
+
+ it { is_expected.to match(expected_result) }
+ end
+
+ describe '#task_list_items' do
+ where(issuable_type: [:issue, :merge_request])
+
+ with_them do
+ let(:issuable) { build(issuable_type, description: description) }
+
+ subject(:result) { issuable.task_list_items }
+
+ context 'when description is present' do
+ let(:description) { 'markdown' }
+
+ it 'gets tasks from markdown' do
+ expect(described_class).to receive(:get_tasks)
+
+ result
+ end
+ end
+
+ context 'when description is blank' do
+ let(:description) { '' }
+
+ it 'returns empty array' do
+ expect(result).to be_empty
+ end
+
+ it 'does not try to get tasks from markdown' do
+ expect(described_class).not_to receive(:get_tasks)
+
+ result
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 4bdb3e0a32a..2e82a12a61a 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -289,4 +289,142 @@ RSpec.describe Ci::Build, 'TokenAuthenticatable' do
expect(build.read_attribute('token')).to be_nil
end
end
+
+ describe '#token_with_expiration' do
+ describe '#expirable?' do
+ subject { build.token_with_expiration.expirable? }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
+
+RSpec.describe Ci::Runner, 'TokenAuthenticatable', :freeze_time do
+ let_it_be(:non_expirable_runner) { create(:ci_runner) }
+ let_it_be(:non_expired_runner) { create(:ci_runner).tap { |r| r.update!(token_expires_at: 5.seconds.from_now) } }
+ let_it_be(:expired_runner) { create(:ci_runner).tap { |r| r.update!(token_expires_at: 5.seconds.ago) } }
+
+ describe '#token_expired?' do
+ subject { runner.token_expired? }
+
+ context 'when enforce_runner_token_expires_at feature flag is disabled' do
+ before do
+ stub_feature_flags(enforce_runner_token_expires_at: false)
+ end
+
+ context 'when runner has no token expiration' do
+ let(:runner) { non_expirable_runner }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when runner token is not expired' do
+ let(:runner) { non_expired_runner }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when runner token is expired' do
+ let(:runner) { expired_runner }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'when enforce_runner_token_expires_at feature flag is enabled' do
+ before do
+ stub_feature_flags(enforce_runner_token_expires_at: true)
+ end
+
+ context 'when runner has no token expiration' do
+ let(:runner) { non_expirable_runner }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when runner token is not expired' do
+ let(:runner) { non_expired_runner }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when runner token is expired' do
+ let(:runner) { expired_runner }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+ end
+
+ describe '#token_with_expiration' do
+ describe '#token' do
+ subject { non_expired_runner.token_with_expiration.token }
+
+ it { is_expected.to eq(non_expired_runner.token) }
+ end
+
+ describe '#token_expires_at' do
+ subject { non_expired_runner.token_with_expiration.token_expires_at }
+
+ it { is_expected.to eq(non_expired_runner.token_expires_at) }
+ end
+
+ describe '#expirable?' do
+ subject { non_expired_runner.token_with_expiration.expirable? }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ describe '.find_by_token' do
+ subject { Ci::Runner.find_by_token(runner.token) }
+
+ context 'when enforce_runner_token_expires_at feature flag is disabled' do
+ before do
+ stub_feature_flags(enforce_runner_token_expires_at: false)
+ end
+
+ context 'when runner has no token expiration' do
+ let(:runner) { non_expirable_runner }
+
+ it { is_expected.to eq(non_expirable_runner) }
+ end
+
+ context 'when runner token is not expired' do
+ let(:runner) { non_expired_runner }
+
+ it { is_expected.to eq(non_expired_runner) }
+ end
+
+ context 'when runner token is expired' do
+ let(:runner) { expired_runner }
+
+ it { is_expected.to eq(expired_runner) }
+ end
+ end
+
+ context 'when enforce_runner_token_expires_at feature flag is enabled' do
+ before do
+ stub_feature_flags(enforce_runner_token_expires_at: true)
+ end
+
+ context 'when runner has no token expiration' do
+ let(:runner) { non_expirable_runner }
+
+ it { is_expected.to eq(non_expirable_runner) }
+ end
+
+ context 'when runner token is not expired' do
+ let(:runner) { non_expired_runner }
+
+ it { is_expected.to eq(non_expired_runner) }
+ end
+
+ context 'when runner token is expired' do
+ let(:runner) { expired_runner }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index b311e302a31..1772fd0ff95 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -23,6 +23,8 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
let(:options) { { encrypted: :required } }
it 'finds the encrypted resource by cleartext' do
+ allow(model).to receive(:where)
+ .and_return(model)
allow(model).to receive(:find_by)
.with('some_field_encrypted' => [encrypted, encrypted_with_static_iv])
.and_return('encrypted resource')
@@ -36,6 +38,8 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
let(:options) { { encrypted: :optional } }
it 'finds the encrypted resource by cleartext' do
+ allow(model).to receive(:where)
+ .and_return(model)
allow(model).to receive(:find_by)
.with('some_field_encrypted' => [encrypted, encrypted_with_static_iv])
.and_return('encrypted resource')
@@ -49,6 +53,8 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
.to receive(:find_token_authenticatable)
.and_return('plaintext resource')
+ allow(model).to receive(:where)
+ .and_return(model)
allow(model).to receive(:find_by)
.with('some_field_encrypted' => [encrypted, encrypted_with_static_iv])
.and_return(nil)
@@ -62,6 +68,8 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
let(:options) { { encrypted: :migrating } }
it 'finds the cleartext resource by cleartext' do
+ allow(model).to receive(:where)
+ .and_return(model)
allow(model).to receive(:find_by)
.with('some_field' => 'my-value')
.and_return('cleartext resource')
@@ -71,6 +79,8 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
end
it 'returns nil if resource cannot be found' do
+ allow(model).to receive(:where)
+ .and_return(model)
allow(model).to receive(:find_by)
.with('some_field' => 'my-value')
.and_return(nil)