diff options
Diffstat (limited to 'spec/models/concerns/bulk_insert_safe_spec.rb')
-rw-r--r-- | spec/models/concerns/bulk_insert_safe_spec.rb | 146 |
1 files changed, 84 insertions, 62 deletions
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb index 0cc355ea694..a8e56cb8bdd 100644 --- a/spec/models/concerns/bulk_insert_safe_spec.rb +++ b/spec/models/concerns/bulk_insert_safe_spec.rb @@ -22,6 +22,18 @@ describe BulkInsertSafe do algorithm: 'aes-256-gcm', key: Settings.attr_encrypted_db_key_base_32, insecure_mode: false + + default_value_for :enum_value, 'case_1' + default_value_for :secret_value, 'my-secret' + default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' + + def self.valid_list(count) + Array.new(count) { |n| new(name: "item-#{n}") } + end + + def self.invalid_list(count) + Array.new(count) { new } + end end module InheritedUnsafeMethods @@ -48,6 +60,8 @@ describe BulkInsertSafe do t.text :encrypted_secret_value, null: false t.string :encrypted_secret_value_iv, null: false t.binary :sha_value, null: false, limit: 20 + + t.index :name, unique: true end end @@ -60,87 +74,95 @@ describe BulkInsertSafe do end end - def build_valid_items_for_bulk_insertion - Array.new(10) do |n| - BulkInsertItem.new( - name: "item-#{n}", - enum_value: 'case_1', - secret_value: 'my-secret', - sha_value: '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' - ) + describe BulkInsertItem do + it_behaves_like 'a BulkInsertSafe model', described_class do + let(:valid_items_for_bulk_insertion) { described_class.valid_list(10) } + let(:invalid_items_for_bulk_insertion) { described_class.invalid_list(10) } end - end - def build_invalid_items_for_bulk_insertion - Array.new(10) do - BulkInsertItem.new( - name: nil, # requires `name` to be set - enum_value: 'case_1', - secret_value: 'my-secret', - sha_value: '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' - ) + context 'when inheriting class methods' do + it 'raises an error when method is not bulk-insert safe' do + expect { described_class.include(InheritedUnsafeMethods) } + .to raise_error(described_class::MethodNotAllowedError) + end + + it 'does not raise an error when method is bulk-insert safe' do + expect { described_class.include(InheritedSafeMethods) }.not_to raise_error + end end - end - it_behaves_like 'a BulkInsertSafe model', BulkInsertItem do - let(:valid_items_for_bulk_insertion) { build_valid_items_for_bulk_insertion } - let(:invalid_items_for_bulk_insertion) { build_invalid_items_for_bulk_insertion } - end + context 'primary keys' do + it 'raises error if primary keys are set prior to insertion' do + item = described_class.new(name: 'valid', id: 10) - context 'when inheriting class methods' do - it 'raises an error when method is not bulk-insert safe' do - expect { BulkInsertItem.include(InheritedUnsafeMethods) }.to( - raise_error(subject::MethodNotAllowedError)) + expect { described_class.bulk_insert!([item]) } + .to raise_error(described_class::PrimaryKeySetError) + end end - it 'does not raise an error when method is bulk-insert safe' do - expect { BulkInsertItem.include(InheritedSafeMethods) }.not_to raise_error - end - end + describe '.bulk_insert!' do + it 'inserts items in the given number of batches' do + items = described_class.valid_list(10) + + expect(ActiveRecord::InsertAll).to receive(:new).twice.and_call_original - context 'primary keys' do - it 'raises error if primary keys are set prior to insertion' do - items = build_valid_items_for_bulk_insertion - items.each_with_index do |item, n| - item.id = n + described_class.bulk_insert!(items, batch_size: 5) end - expect { BulkInsertItem.bulk_insert!(items) }.to raise_error(subject::PrimaryKeySetError) - end - end + it 'items can be properly fetched from database' do + items = described_class.valid_list(10) - describe '.bulk_insert!' do - it 'inserts items in the given number of batches' do - items = build_valid_items_for_bulk_insertion - expect(items.size).to eq(10) - expect(BulkInsertItem).to receive(:insert_all!).twice + described_class.bulk_insert!(items) - BulkInsertItem.bulk_insert!(items, batch_size: 5) - end + attribute_names = described_class.attribute_names - %w[id] + expect(described_class.last(items.size).pluck(*attribute_names)).to eq( + items.pluck(*attribute_names)) + end - it 'items can be properly fetched from database' do - items = build_valid_items_for_bulk_insertion + it 'rolls back the transaction when any item is invalid' do + # second batch is bad + all_items = described_class.valid_list(10) + + described_class.invalid_list(10) - BulkInsertItem.bulk_insert!(items) + expect do + described_class.bulk_insert!(all_items, batch_size: 2) rescue nil + end.not_to change { described_class.count } + end - attribute_names = BulkInsertItem.attribute_names - %w[id] - expect(BulkInsertItem.last(items.size).pluck(*attribute_names)).to eq( - items.pluck(*attribute_names)) + it 'does nothing and returns true when items are empty' do + expect(described_class.bulk_insert!([])).to be(true) + expect(described_class.count).to eq(0) + end end - it 'rolls back the transaction when any item is invalid' do - # second batch is bad - all_items = build_valid_items_for_bulk_insertion + build_invalid_items_for_bulk_insertion - batch_size = all_items.size / 2 + context 'when duplicate items are to be inserted' do + let!(:existing_object) { described_class.create!(name: 'duplicate', secret_value: 'old value') } + let(:new_object) { described_class.new(name: 'duplicate', secret_value: 'new value') } + + describe '.bulk_insert!' do + context 'when skip_duplicates is set to false' do + it 'raises an exception' do + expect { described_class.bulk_insert!([new_object], skip_duplicates: false) } + .to raise_error(ActiveRecord::RecordNotUnique) + end + end + + context 'when skip_duplicates is set to true' do + it 'does not update existing object' do + described_class.bulk_insert!([new_object], skip_duplicates: true) + + expect(existing_object.reload.secret_value).to eq('old value') + end + end + end - expect do - BulkInsertItem.bulk_insert!(all_items, batch_size: batch_size) rescue nil - end.not_to change { BulkInsertItem.count } - end + describe '.bulk_upsert!' do + it 'updates existing object' do + described_class.bulk_upsert!([new_object], unique_by: %w[name]) - it 'does nothing and returns true when items are empty' do - expect(BulkInsertItem.bulk_insert!([])).to be(true) - expect(BulkInsertItem.count).to eq(0) + expect(existing_object.reload.secret_value).to eq('new value') + end + end end end end |