summaryrefslogtreecommitdiff
path: root/spec/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/concerns')
-rw-r--r--spec/models/concerns/atomic_internal_id_spec.rb152
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb7
-rw-r--r--spec/models/concerns/featurable_spec.rb16
-rw-r--r--spec/models/concerns/issuable_spec.rb19
-rw-r--r--spec/models/concerns/nullify_if_blank_spec.rb51
-rw-r--r--spec/models/concerns/protected_ref_spec.rb77
-rw-r--r--spec/models/concerns/spammable_spec.rb55
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb4
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb8
9 files changed, 353 insertions, 36 deletions
diff --git a/spec/models/concerns/atomic_internal_id_spec.rb b/spec/models/concerns/atomic_internal_id_spec.rb
index 5ee3c012dc9..35b0f107676 100644
--- a/spec/models/concerns/atomic_internal_id_spec.rb
+++ b/spec/models/concerns/atomic_internal_id_spec.rb
@@ -87,6 +87,158 @@ RSpec.describe AtomicInternalId do
end
end
+ describe '#clear_scope_iid!' do
+ context 'when no ensure_if condition is given' do
+ it 'clears automatically set IIDs' do
+ expect(milestone).to receive(:clear_project_iid!).and_call_original
+
+ expect_iid_to_be_set_and_rollback(milestone)
+
+ expect(milestone.iid).to be_nil
+ end
+
+ it 'does not clear manually set IIDS' do
+ milestone.iid = external_iid
+
+ expect(milestone).to receive(:clear_project_iid!).and_call_original
+
+ expect_iid_to_be_set_and_rollback(milestone)
+
+ expect(milestone.iid).to eq(external_iid)
+ end
+ end
+
+ context 'when an ensure_if condition is given' do
+ let(:test_class) do
+ Class.new(ApplicationRecord) do
+ include AtomicInternalId
+ include Importable
+
+ self.table_name = :milestones
+
+ belongs_to :project
+
+ has_internal_id :iid, scope: :project, track_if: -> { !importing }, ensure_if: -> { !importing }
+
+ def self.name
+ 'TestClass'
+ end
+ end
+ end
+
+ let(:instance) { test_class.new(milestone.attributes) }
+
+ context 'when the ensure_if condition evaluates to true' do
+ it 'clears automatically set IIDs' do
+ expect(instance).to receive(:clear_project_iid!).and_call_original
+
+ expect_iid_to_be_set_and_rollback(instance)
+
+ expect(instance.iid).to be_nil
+ end
+
+ it 'does not clear manually set IIDs' do
+ instance.iid = external_iid
+
+ expect(instance).to receive(:clear_project_iid!).and_call_original
+
+ expect_iid_to_be_set_and_rollback(instance)
+
+ expect(instance.iid).to eq(external_iid)
+ end
+ end
+
+ context 'when the ensure_if condition evaluates to false' do
+ before do
+ instance.importing = true
+ end
+
+ it 'does not clear IIDs' do
+ instance.iid = external_iid
+
+ expect(instance).not_to receive(:clear_project_iid!)
+
+ expect_iid_to_be_set_and_rollback(instance)
+
+ expect(instance.iid).to eq(external_iid)
+ end
+ end
+ end
+
+ def expect_iid_to_be_set_and_rollback(instance)
+ ActiveRecord::Base.transaction(requires_new: true) do
+ instance.save!
+
+ expect(instance.iid).not_to be_nil
+
+ raise ActiveRecord::Rollback
+ end
+ end
+ end
+
+ describe '#validate_scope_iid_exists!' do
+ let(:test_class) do
+ Class.new(ApplicationRecord) do
+ include AtomicInternalId
+ include Importable
+
+ self.table_name = :milestones
+
+ belongs_to :project
+
+ def self.name
+ 'TestClass'
+ end
+ end
+ end
+
+ let(:instance) { test_class.new(milestone.attributes) }
+
+ before do
+ test_class.has_internal_id :iid, scope: :project, presence: presence, ensure_if: -> { !importing }
+
+ instance.importing = true
+ end
+
+ context 'when the presence flag is set' do
+ let(:presence) { true }
+
+ it 'raises an error for blank iids on create' do
+ expect do
+ instance.save!
+ end.to raise_error(described_class::MissingValueError, 'iid was unexpectedly blank!')
+ end
+
+ it 'raises an error for blank iids on update' do
+ instance.iid = 100
+ instance.save!
+
+ instance.iid = nil
+
+ expect do
+ instance.save!
+ end.to raise_error(described_class::MissingValueError, 'iid was unexpectedly blank!')
+ end
+ end
+
+ context 'when the presence flag is not set' do
+ let(:presence) { false }
+
+ it 'does not raise an error for blank iids on create' do
+ expect { instance.save! }.not_to raise_error
+ end
+
+ it 'does not raise an error for blank iids on update' do
+ instance.iid = 100
+ instance.save!
+
+ instance.iid = nil
+
+ expect { instance.save! }.not_to raise_error
+ end
+ end
+ end
+
describe '.with_project_iid_supply' do
let(:iid) { 100 }
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index 82b0c00b396..e40b0cf11ff 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -44,7 +44,6 @@ RSpec.describe BulkInsertSafe do
insecure_mode: false
default_value_for :enum_value, 'case_1'
- default_value_for :secret_value, 'my-secret'
default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'
default_value_for :jsonb_value, { "key" => "value" }
@@ -53,11 +52,11 @@ RSpec.describe BulkInsertSafe do
end
def self.valid_list(count)
- Array.new(count) { |n| new(name: "item-#{n}") }
+ Array.new(count) { |n| new(name: "item-#{n}", secret_value: 'my-secret') }
end
def self.invalid_list(count)
- Array.new(count) { new }
+ Array.new(count) { new(secret_value: 'my-secret') }
end
end
end
@@ -102,7 +101,7 @@ RSpec.describe BulkInsertSafe do
context 'primary keys' do
it 'raises error if primary keys are set prior to insertion' do
- item = bulk_insert_item_class.new(name: 'valid', id: 10)
+ item = bulk_insert_item_class.new(name: 'valid', id: 10, secret_value: 'my-secret')
expect { bulk_insert_item_class.bulk_insert!([item]) }
.to raise_error(bulk_insert_item_class::PrimaryKeySetError)
diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb
index 99acc563950..b550d22f686 100644
--- a/spec/models/concerns/featurable_spec.rb
+++ b/spec/models/concerns/featurable_spec.rb
@@ -134,22 +134,6 @@ RSpec.describe Featurable do
expect(project.feature_available?(:issues, user)).to eq(true)
end
end
-
- context 'when feature is disabled by a feature flag' do
- it 'returns false' do
- stub_feature_flags(issues: false)
-
- expect(project.feature_available?(:issues, user)).to eq(false)
- end
- end
-
- context 'when feature is enabled by a feature flag' do
- it 'returns true' do
- stub_feature_flags(issues: true)
-
- expect(project.feature_available?(:issues, user)).to eq(true)
- end
- end
end
describe '#*_enabled?' do
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index ff5b270cf33..3545c8e9686 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Issuable do
include ProjectForksHelper
+ using RSpec::Parameterized::TableSyntax
let(:issuable_class) { Issue }
let(:issue) { create(:issue, title: 'An issue', description: 'A description') }
@@ -45,13 +46,17 @@ RSpec.describe Issuable do
end
it { is_expected.to validate_presence_of(:project) }
- it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_length_of(:title).is_at_most(described_class::TITLE_LENGTH_MAX) }
it { is_expected.to validate_length_of(:description).is_at_most(described_class::DESCRIPTION_LENGTH_MAX).on(:create) }
- it_behaves_like 'validates description length with custom validation'
+ it_behaves_like 'validates description length with custom validation' do
+ before do
+ allow(InternalId).to receive(:generate_next).and_call_original
+ end
+ end
+
it_behaves_like 'truncates the description to its allowed maximum length on import'
end
end
@@ -820,8 +825,6 @@ RSpec.describe Issuable do
end
describe '#supports_time_tracking?' do
- using RSpec::Parameterized::TableSyntax
-
where(:issuable_type, :supports_time_tracking) do
:issue | true
:incident | true
@@ -838,8 +841,6 @@ RSpec.describe Issuable do
end
describe '#supports_severity?' do
- using RSpec::Parameterized::TableSyntax
-
where(:issuable_type, :supports_severity) do
:issue | false
:incident | true
@@ -856,8 +857,6 @@ RSpec.describe Issuable do
end
describe '#incident?' do
- using RSpec::Parameterized::TableSyntax
-
where(:issuable_type, :incident) do
:issue | false
:incident | true
@@ -874,8 +873,6 @@ RSpec.describe Issuable do
end
describe '#supports_issue_type?' do
- using RSpec::Parameterized::TableSyntax
-
where(:issuable_type, :supports_issue_type) do
:issue | true
:merge_request | false
@@ -894,8 +891,6 @@ RSpec.describe Issuable do
subject { issuable.severity }
context 'when issuable is not an incident' do
- using RSpec::Parameterized::TableSyntax
-
where(:issuable_type, :severity) do
:issue | 'unknown'
:merge_request | 'unknown'
diff --git a/spec/models/concerns/nullify_if_blank_spec.rb b/spec/models/concerns/nullify_if_blank_spec.rb
new file mode 100644
index 00000000000..2d1bdba39dd
--- /dev/null
+++ b/spec/models/concerns/nullify_if_blank_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe NullifyIfBlank do
+ let_it_be(:model) do
+ Class.new(ApplicationRecord) do
+ include NullifyIfBlank
+
+ nullify_if_blank :name
+
+ self.table_name = 'users'
+ end
+ end
+
+ context 'attribute exists' do
+ let(:instance) { model.new(name: name) }
+
+ subject { instance.name }
+
+ before do
+ instance.validate
+ end
+
+ context 'attribute is blank' do
+ let(:name) { '' }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'attribute is nil' do
+ let(:name) { nil }
+
+ it { is_expected.to be_nil}
+ end
+
+ context 'attribute is not blank' do
+ let(:name) { 'name' }
+
+ it { is_expected.to eq('name') }
+ end
+ end
+
+ context 'attribute does not exist' do
+ before do
+ model.table_name = 'issues'
+ end
+
+ it { expect { model.new.valid? }.to raise_error(ActiveModel::UnknownAttributeError) }
+ end
+end
diff --git a/spec/models/concerns/protected_ref_spec.rb b/spec/models/concerns/protected_ref_spec.rb
new file mode 100644
index 00000000000..0a020736269
--- /dev/null
+++ b/spec/models/concerns/protected_ref_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ProtectedRef do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user, maintainer_projects: [project]) }
+
+ where(:klass, :factory, :action) do
+ ProtectedBranch | :protected_branch | :push
+ ProtectedTag | :protected_tag | :create
+ end
+
+ with_them do
+ describe '#protected_ref_accessible_to?' do
+ subject do
+ klass.protected_ref_accessible_to?('release', user, project: project, action: action)
+ end
+
+ it 'user cannot do action if rules do not exist' do
+ is_expected.to be_falsy
+ end
+
+ context 'the ref is protected' do
+ let!(:default_rule) { create(factory, :"developers_can_#{action}", project: project, name: 'release') }
+
+ context 'all rules permit action' do
+ let!(:maintainers_can) { create(factory, :"maintainers_can_#{action}", project: project, name: 'release*') }
+
+ it 'user can do action' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'one of the rules forbids action' do
+ let!(:no_one_can) { create(factory, :"no_one_can_#{action}", project: project, name: 'release*') }
+
+ it 'user cannot do action' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+ end
+
+ describe '#developers_can?' do
+ subject do
+ klass.developers_can?(action, 'release')
+ end
+
+ it 'developers cannot do action if rules do not exist' do
+ is_expected.to be_falsy
+ end
+
+ context 'the ref is protected' do
+ let!(:default_rule) { create(factory, :"developers_can_#{action}", project: project, name: 'release') }
+
+ context 'all rules permit developers to do action' do
+ let!(:developers_can) { create(factory, :"developers_can_#{action}", project: project, name: 'release*') }
+
+ it 'developers can do action' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'one of the rules forbids developers to do action' do
+ let!(:maintainers_can) { create(factory, :"maintainers_can_#{action}", project: project, name: 'release*') }
+
+ it 'developers cannot do action' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb
index d4fcb2e99eb..3c5f3b2d2ad 100644
--- a/spec/models/concerns/spammable_spec.rb
+++ b/spec/models/concerns/spammable_spec.rb
@@ -120,6 +120,61 @@ RSpec.describe Spammable do
end
end
+ describe '#render_recaptcha?' do
+ before do
+ allow(Gitlab::Recaptcha).to receive(:enabled?) { recaptcha_enabled }
+ end
+
+ context 'when recaptcha is not enabled' do
+ let(:recaptcha_enabled) { false }
+
+ it 'returns false' do
+ expect(issue.render_recaptcha?).to eq(false)
+ end
+ end
+
+ context 'when recaptcha is enabled' do
+ let(:recaptcha_enabled) { true }
+
+ context 'when there are two or more errors' do
+ before do
+ issue.errors.add(:base, 'a spam error')
+ issue.errors.add(:base, 'some other error')
+ end
+
+ it 'returns false' do
+ expect(issue.render_recaptcha?).to eq(false)
+ end
+ end
+
+ context 'when there are less than two errors' do
+ before do
+ issue.errors.add(:base, 'a spam error')
+ end
+
+ context 'when spammable does not need recaptcha' do
+ before do
+ issue.needs_recaptcha = false
+ end
+
+ it 'returns false' do
+ expect(issue.render_recaptcha?).to eq(false)
+ end
+ end
+
+ context 'when spammable needs recaptcha' do
+ before do
+ issue.needs_recaptcha!
+ end
+
+ it 'returns false' do
+ expect(issue.render_recaptcha?).to eq(true)
+ end
+ end
+ end
+ end
+ end
+
describe '#clear_spam_flags!' do
it 'clears spam and recaptcha flags' do
issue.spam = true
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index d8b77e1cd0d..2df76684d71 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe ApplicationSetting, 'TokenAuthenticatable' do
it 'persists new token as an encrypted string' do
expect(subject).to eq settings.reload.runners_registration_token
expect(settings.read_attribute('runners_registration_token_encrypted'))
- .to eq Gitlab::CryptoHelper.aes256_gcm_encrypt(subject)
+ .to eq Gitlab::CryptoHelper.aes256_gcm_encrypt(subject, nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC)
expect(settings).to be_persisted
end
@@ -243,7 +243,7 @@ RSpec.describe Ci::Build, 'TokenAuthenticatable' do
it 'persists new token as an encrypted string' do
build.ensure_token!
- encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(build.token)
+ encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(build.token, nonce: Gitlab::CryptoHelper::AES256_GCM_IV_STATIC)
expect(build.read_attribute('token_encrypted')).to eq encrypted
end
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index f6b8cf7def4..1e1cd97e410 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -68,6 +68,10 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
context 'when using optional strategy' do
let(:options) { { encrypted: :optional } }
+ before do
+ stub_feature_flags(dynamic_nonce_creation: false)
+ end
+
it 'returns decrypted token when an encrypted token is present' do
allow(instance).to receive(:read_attribute)
.with('some_field_encrypted')
@@ -124,7 +128,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
it 'writes encrypted token and removes plaintext token and returns it' do
expect(instance).to receive(:[]=)
- .with('some_field_encrypted', encrypted)
+ .with('some_field_encrypted', any_args)
expect(instance).to receive(:[]=)
.with('some_field', nil)
@@ -137,7 +141,7 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
it 'writes encrypted token and writes plaintext token' do
expect(instance).to receive(:[]=)
- .with('some_field_encrypted', encrypted)
+ .with('some_field_encrypted', any_args)
expect(instance).to receive(:[]=)
.with('some_field', 'my-value')