diff options
Diffstat (limited to 'spec/models/concerns')
-rw-r--r-- | spec/models/concerns/awardable_spec.rb | 4 | ||||
-rw-r--r-- | spec/models/concerns/bulk_insert_safe_spec.rb | 31 | ||||
-rw-r--r-- | spec/models/concerns/deployment_platform_spec.rb | 32 | ||||
-rw-r--r-- | spec/models/concerns/has_timelogs_report_spec.rb | 55 | ||||
-rw-r--r-- | spec/models/concerns/has_user_type_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/concerns/integrations/has_data_fields_spec.rb | 156 | ||||
-rw-r--r-- | spec/models/concerns/issuable_spec.rb | 12 | ||||
-rw-r--r-- | spec/models/concerns/limitable_spec.rb | 25 | ||||
-rw-r--r-- | spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb | 94 |
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 |