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/concerns')
-rw-r--r--spec/models/concerns/awardable_spec.rb4
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb31
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb32
-rw-r--r--spec/models/concerns/has_timelogs_report_spec.rb55
-rw-r--r--spec/models/concerns/has_user_type_spec.rb2
-rw-r--r--spec/models/concerns/integrations/has_data_fields_spec.rb156
-rw-r--r--spec/models/concerns/issuable_spec.rb12
-rw-r--r--spec/models/concerns/limitable_spec.rb25
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb94
9 files changed, 314 insertions, 97 deletions
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
index b5b3772ecb6..b80b6ec95e2 100644
--- a/spec/models/concerns/awardable_spec.rb
+++ b/spec/models/concerns/awardable_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe Awardable do
it "doesn't include unused thumbs buttons when disabled in project" do
issue_without_downvote.project.show_default_award_emojis = false
- expect(issue_without_downvote.grouped_awards.keys.sort).to eq []
+ expect(issue_without_downvote.grouped_awards.keys.sort).to be_empty
end
it "includes unused thumbs buttons when enabled in project" do
@@ -118,7 +118,7 @@ RSpec.describe Awardable do
end
it "doesn't include unused thumbs buttons in summary" do
- expect(issue_without_downvote.grouped_awards(with_thumbs: false).keys).to eq []
+ expect(issue_without_downvote.grouped_awards(with_thumbs: false).keys).to be_empty
end
it "includes used thumbs buttons when disabled in project" do
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index ca6df506ee8..209ee1264d5 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -20,6 +20,13 @@ RSpec.describe BulkInsertSafe do
t.index :name, unique: true
end
+
+ create_table :bulk_insert_items_with_composite_pk, id: false, force: true do |t|
+ t.integer :id, null: true
+ t.string :name, null: true
+ end
+
+ execute("ALTER TABLE bulk_insert_items_with_composite_pk ADD PRIMARY KEY (id,name);")
end
end
@@ -27,6 +34,7 @@ RSpec.describe BulkInsertSafe do
ActiveRecord::Schema.define do
drop_table :bulk_insert_items, force: true
drop_table :bulk_insert_parent_items, force: true
+ drop_table :bulk_insert_items_with_composite_pk, force: true
end
end
@@ -227,5 +235,28 @@ RSpec.describe BulkInsertSafe do
end
end
end
+
+ context 'when a model with composite primary key is inserted' do
+ let_it_be(:bulk_insert_items_with_composite_pk_class) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'bulk_insert_items_with_composite_pk'
+
+ include BulkInsertSafe
+ end
+ end
+
+ let(:new_object) { bulk_insert_items_with_composite_pk_class.new(id: 1, name: 'composite') }
+
+ it 'successfully inserts an item' do
+ expect(ActiveRecord::InsertAll).to receive(:new)
+ .with(
+ bulk_insert_items_with_composite_pk_class, [new_object.as_json], on_duplicate: :raise, returning: false, unique_by: %w[id name]
+ ).and_call_original
+
+ expect { bulk_insert_items_with_composite_pk_class.bulk_insert!([new_object]) }.to(
+ change(bulk_insert_items_with_composite_pk_class, :count).from(0).to(1)
+ )
+ end
+ end
end
end
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index 2bb6aa27e21..7fa55184cf1 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -254,20 +254,8 @@ RSpec.describe DeploymentPlatform do
create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
end
- context 'cluster_management_project feature is enabled' do
- it 'returns the cluster with management project' do
- is_expected.to eq(cluster_with_management_project.platform_kubernetes)
- end
- end
-
- context 'cluster_management_project feature is disabled' do
- before do
- stub_feature_flags(cluster_management_project: false)
- end
-
- it 'returns nothing' do
- is_expected.to be_nil
- end
+ it 'returns the cluster with management project' do
+ is_expected.to eq(cluster_with_management_project.platform_kubernetes)
end
end
@@ -311,20 +299,8 @@ RSpec.describe DeploymentPlatform do
create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
end
- context 'cluster_management_project feature is enabled' do
- it 'returns the cluster with management project' do
- is_expected.to eq(cluster_with_management_project.platform_kubernetes)
- end
- end
-
- context 'cluster_management_project feature is disabled' do
- before do
- stub_feature_flags(cluster_management_project: false)
- end
-
- it 'returns the group cluster' do
- is_expected.to eq(group_cluster.platform_kubernetes)
- end
+ it 'returns the cluster with management project' do
+ is_expected.to eq(cluster_with_management_project.platform_kubernetes)
end
end
diff --git a/spec/models/concerns/has_timelogs_report_spec.rb b/spec/models/concerns/has_timelogs_report_spec.rb
deleted file mode 100644
index f0dca47fae1..00000000000
--- a/spec/models/concerns/has_timelogs_report_spec.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe HasTimelogsReport do
- let_it_be(:user) { create(:user) }
-
- let(:group) { create(:group) }
- let(:project) { create(:project, :public, group: group) }
- let(:issue1) { create(:issue, project: project) }
- let(:merge_request1) { create(:merge_request, source_project: project) }
-
- describe '#timelogs' do
- let_it_be(:start_time) { 20.days.ago }
- let_it_be(:end_time) { 8.days.ago }
-
- let!(:timelog1) { create_timelog(15.days.ago, issue: issue1) }
- let!(:timelog2) { create_timelog(10.days.ago, merge_request: merge_request1) }
- let!(:timelog3) { create_timelog(5.days.ago, issue: issue1) }
-
- before do
- group.add_developer(user)
- end
-
- it 'returns collection of timelogs between given times' do
- expect(group.timelogs(start_time, end_time).to_a).to match_array([timelog1, timelog2])
- end
-
- it 'returns empty collection if times are not present' do
- expect(group.timelogs(nil, nil)).to be_empty
- end
-
- it 'returns empty collection if time range is invalid' do
- expect(group.timelogs(end_time, start_time)).to be_empty
- end
- end
-
- describe '#user_can_access_group_timelogs?' do
- it 'returns true if user can access group timelogs' do
- group.add_developer(user)
-
- expect(group).to be_user_can_access_group_timelogs(user)
- end
-
- it 'returns false if user has insufficient permissions' do
- group.add_guest(user)
-
- expect(group).not_to be_user_can_access_group_timelogs(user)
- end
- end
-
- def create_timelog(time, issue: nil, merge_request: nil)
- create(:timelog, issue: issue, merge_request: merge_request, user: user, spent_at: time)
- end
-end
diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb
index c87bbf24c30..a6a0e074589 100644
--- a/spec/models/concerns/has_user_type_spec.rb
+++ b/spec/models/concerns/has_user_type_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe User do
specify 'types consistency checks', :aggregate_failures do
expect(described_class::USER_TYPES.keys)
- .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot])
+ .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot])
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
diff --git a/spec/models/concerns/integrations/has_data_fields_spec.rb b/spec/models/concerns/integrations/has_data_fields_spec.rb
new file mode 100644
index 00000000000..54e0ac9c5a5
--- /dev/null
+++ b/spec/models/concerns/integrations/has_data_fields_spec.rb
@@ -0,0 +1,156 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HasDataFields do
+ let(:url) { 'http://url.com' }
+ let(:username) { 'username_one' }
+ let(:properties) do
+ { url: url, username: username }
+ end
+
+ shared_examples 'data fields' do
+ describe '#arg' do
+ it 'returns an argument correctly' do
+ expect(service.url).to eq(url)
+ end
+ end
+
+ describe '{arg}_changed?' do
+ it 'returns false when the property has not been assigned a new value' do
+ service.username = 'new_username'
+ service.validate
+ expect(service.url_changed?).to be_falsy
+ end
+
+ it 'returns true when the property has been assigned a different value' do
+ service.url = "http://example.com"
+ service.validate
+ expect(service.url_changed?).to be_truthy
+ end
+
+ it 'returns true when the property has been assigned a different value twice' do
+ service.url = "http://example.com"
+ service.url = "http://example.com"
+ service.validate
+ expect(service.url_changed?).to be_truthy
+ end
+
+ it 'returns false when the property has been re-assigned the same value' do
+ service.url = 'http://url.com'
+ service.validate
+ expect(service.url_changed?).to be_falsy
+ end
+ end
+
+ describe '{arg}_touched?' do
+ it 'returns false when the property has not been assigned a new value' do
+ service.username = 'new_username'
+ service.validate
+ expect(service.url_changed?).to be_falsy
+ end
+
+ it 'returns true when the property has been assigned a different value' do
+ service.url = "http://example.com"
+ service.validate
+ expect(service.url_changed?).to be_truthy
+ end
+
+ it 'returns true when the property has been assigned a different value twice' do
+ service.url = "http://example.com"
+ service.url = "http://example.com"
+ service.validate
+ expect(service.url_changed?).to be_truthy
+ end
+
+ it 'returns true when the property has been re-assigned the same value' do
+ service.url = 'http://url.com'
+ expect(service.url_touched?).to be_truthy
+ end
+
+ it 'returns false when the property has been re-assigned the same value' do
+ service.url = 'http://url.com'
+ service.validate
+ expect(service.url_changed?).to be_falsy
+ end
+ end
+
+ describe 'data_fields_present?' do
+ it 'returns true from the issue tracker service' do
+ expect(service.data_fields_present?).to be true
+ end
+ end
+ end
+
+ context 'when data are stored in data_fields' do
+ let(:service) do
+ create(:jira_service, url: url, username: username)
+ end
+
+ it_behaves_like 'data fields'
+
+ describe '{arg}_was?' do
+ it 'returns nil' do
+ service.url = 'http://example.com'
+ service.validate
+ expect(service.url_was).to be_nil
+ end
+ end
+ end
+
+ context 'when service and data_fields are not persisted' do
+ let(:service) do
+ Integrations::Jira.new
+ end
+
+ describe 'data_fields_present?' do
+ it 'returns true' do
+ expect(service.data_fields_present?).to be true
+ end
+ end
+ end
+
+ context 'when data are stored in properties' do
+ let(:service) { create(:jira_service, :without_properties_callback, properties: properties) }
+
+ it_behaves_like 'data fields'
+
+ describe '{arg}_was?' do
+ it 'returns nil when the property has not been assigned a new value' do
+ service.username = 'new_username'
+ service.validate
+ expect(service.url_was).to be_nil
+ end
+
+ it 'returns initial value when the property has been assigned a different value' do
+ service.url = 'http://example.com'
+ service.validate
+ expect(service.url_was).to eq('http://url.com')
+ end
+
+ it 'returns initial value when the property has been re-assigned the same value' do
+ service.url = 'http://url.com'
+ service.validate
+ expect(service.url_was).to eq('http://url.com')
+ end
+ end
+ end
+
+ context 'when data are stored in both properties and data_fields' do
+ let(:service) do
+ create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |integration|
+ create(:jira_tracker_data, properties.merge(integration: integration))
+ end
+ end
+
+ it_behaves_like 'data fields'
+
+ describe '{arg}_was?' do
+ it 'returns nil' do
+ service.url = 'http://example.com'
+ service.validate
+ expect(service.url_was).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 14db9b530db..7b100b7a6f3 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -715,6 +715,12 @@ RSpec.describe Issuable do
expect(issue.total_time_spent).to eq(1800)
end
+ it 'stores the time change' do
+ spend_time(1800)
+
+ expect(issue.time_change).to eq(1800)
+ end
+
it 'updates issues updated_at' do
issue
@@ -735,6 +741,12 @@ RSpec.describe Issuable do
expect(issue.total_time_spent).to eq(900)
end
+ it 'stores negative time change' do
+ spend_time(-900)
+
+ expect(issue.time_change).to eq(-900)
+ end
+
context 'when time to subtract exceeds the total time spent' do
it 'raise a validation error' do
Timecop.travel(1.minute.from_now) do
diff --git a/spec/models/concerns/limitable_spec.rb b/spec/models/concerns/limitable_spec.rb
index 753e2a8ee5e..6b25ed39efb 100644
--- a/spec/models/concerns/limitable_spec.rb
+++ b/spec/models/concerns/limitable_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'active_model'
RSpec.describe Limitable do
let(:minimal_test_class) do
@@ -35,6 +36,28 @@ RSpec.describe Limitable do
instance.valid?(:create)
end
+
+ context 'with custom relation' do
+ before do
+ MinimalTestClass.limit_relation = :custom_relation
+ end
+
+ it 'triggers custom limit_relation' do
+ instance = MinimalTestClass.new
+
+ def instance.project
+ @project ||= Object.new
+ end
+
+ limits = Object.new
+ custom_relation = Object.new
+ expect(instance).to receive(:custom_relation).and_return(custom_relation)
+ expect(instance.project).to receive(:actual_limits).and_return(limits)
+ expect(limits).to receive(:exceeded?).with(instance.class.name.demodulize.tableize, custom_relation).and_return(false)
+
+ instance.valid?(:create)
+ end
+ end
end
context 'with global limit' do
diff --git a/spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb
index 6f322a32a3b..671e51e3913 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb
@@ -3,25 +3,99 @@
require 'spec_helper'
RSpec.describe TokenAuthenticatableStrategies::EncryptionHelper do
- let(:encrypted_token) { described_class.encrypt_token('my-value') }
+ let(:encrypted_token) { described_class.encrypt_token('my-value-my-value-my-value') }
describe '.encrypt_token' do
- it 'encrypts token' do
- expect(encrypted_token).not_to eq('my-value')
+ context 'when dynamic_nonce feature flag is switched on' do
+ it 'adds nonce identifier on the beginning' do
+ expect(encrypted_token.first).to eq(described_class::DYNAMIC_NONCE_IDENTIFIER)
+ end
+
+ it 'adds nonce at the end' do
+ nonce = encrypted_token.last(described_class::NONCE_SIZE)
+
+ expect(nonce).to eq(::Digest::SHA256.hexdigest('my-value-my-value-my-value').bytes.take(described_class::NONCE_SIZE).pack('c*'))
+ end
+
+ it 'encrypts token' do
+ expect(encrypted_token[1...-described_class::NONCE_SIZE]).not_to eq('my-value-my-value-my-value')
+ end
+ end
+
+ context 'when dynamic_nonce feature flag is switched off' do
+ before do
+ stub_feature_flags(dynamic_nonce: false)
+ end
+
+ it 'does not add nonce identifier on the beginning' do
+ expect(encrypted_token.first).not_to eq(described_class::DYNAMIC_NONCE_IDENTIFIER)
+ end
+
+ it 'does not add nonce in the end' do
+ nonce = encrypted_token.last(described_class::NONCE_SIZE)
+
+ expect(nonce).not_to eq(::Digest::SHA256.hexdigest('my-value-my-value-my-value').bytes.take(described_class::NONCE_SIZE).pack('c*'))
+ end
+
+ it 'encrypts token with static iv' do
+ token = Gitlab::CryptoHelper.aes256_gcm_encrypt('my-value-my-value-my-value')
+
+ expect(encrypted_token).to eq(token)
+ end
end
end
describe '.decrypt_token' do
- it 'decrypts token with static iv' do
- expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ context 'with feature flag switched off' do
+ before do
+ stub_feature_flags(dynamic_nonce: false)
+ end
+
+ it 'decrypts token with static iv' do
+ encrypted_token = described_class.encrypt_token('my-value')
+
+ expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ end
+
+ it 'decrypts token if feature flag changed after encryption' do
+ encrypted_token = described_class.encrypt_token('my-value')
+
+ expect(encrypted_token).not_to eq('my-value')
+
+ stub_feature_flags(dynamic_nonce: true)
+
+ expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ end
+
+ it 'decrypts token with dynamic iv' do
+ iv = ::Digest::SHA256.hexdigest('my-value').bytes.take(described_class::NONCE_SIZE).pack('c*')
+ token = Gitlab::CryptoHelper.aes256_gcm_encrypt('my-value', nonce: iv)
+ encrypted_token = "#{described_class::DYNAMIC_NONCE_IDENTIFIER}#{token}#{iv}"
+
+ expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ end
end
- it 'decrypts token with dynamic iv' do
- iv = ::Digest::SHA256.hexdigest('my-value').bytes.take(described_class::NONCE_SIZE).pack('c*')
- token = Gitlab::CryptoHelper.aes256_gcm_encrypt('my-value', nonce: iv)
- encrypted_token = "#{described_class::DYNAMIC_NONCE_IDENTIFIER}#{token}#{iv}"
+ context 'with feature flag switched on' do
+ before do
+ stub_feature_flags(dynamic_nonce: true)
+ end
+
+ it 'decrypts token with dynamic iv' do
+ encrypted_token = described_class.encrypt_token('my-value')
+
+ expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ end
+
+ it 'decrypts token if feature flag changed after encryption' do
+ encrypted_token = described_class.encrypt_token('my-value')
+
+ expect(encrypted_token).not_to eq('my-value')
+
+ stub_feature_flags(dynamic_nonce: false)
- expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ expect(described_class.decrypt_token(encrypted_token)).to eq('my-value')
+ end
end
end
end