# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' describe InsertCustomFieldJoins do describe '.find_or_create' do let(:nonprofit) { force_create(:nonprofit)} let(:other_nonprofit) { force_create(:nonprofit)} let(:supporter) {force_create(:supporter, :nonprofit => nonprofit)} let(:other_supporter){force_create(:supporter, :nonprofit => other_nonprofit)} let(:initial_custom_field_master){ force_create(:custom_field_master, :nonprofit => nonprofit, :name => "CFM Name")} describe 'param validation' do it 'basic validation' do expect {InsertCustomFieldJoins.find_or_create(nil, nil, nil)}.to(raise_error {|error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [ {:key => :np_id, :name => :required}, {:key => :np_id, :name => :is_integer}, {:key => :supporter_ids, :name => :required}, {:key => :supporter_ids, :name => :is_array}, {:key => :supporter_ids, :name => :min_length}, {:key => :field_data, :name => :required}, {:key => :field_data, :name => :is_array}, {:key => :field_data, :name => :min_length} ]) }) end it 'validate nonprofit existence' do expect {InsertCustomFieldJoins.find_or_create(5, [555], [[1, 1]])}.to(raise_error {|error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [ {:key => :np_id} ]) expect(error.message).to eq "5 is not a valid non-profit" }) end it 'validate supporter in nonprofit' do expect {InsertCustomFieldJoins.find_or_create(nonprofit.id, [other_supporter.id], [[1, 1]])}.to(raise_error {|error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [ {:key => :supporter_ids} ]) expect(error.message).to eq "#{other_supporter.id} is not a valid supporter for nonprofit #{nonprofit.id}" }) end end it 'run insert' do new_cf_name = 'new cf name' new_cf_value = 'value' old_cf_value = 'old_cf_value' expect(InsertCustomFieldJoins).to receive(:in_bulk) do |np_id, supporters_id, field_data| expect(np_id).to eq nonprofit.id expect(supporters_id).to eq [supporter.id] expect(field_data.length).to eq 2 expect(field_data).to include( { :custom_field_master_id => initial_custom_field_master.id, :value => old_cf_value}) expect(field_data).to include( { :custom_field_master_id => CustomFieldMaster.where(:name => new_cf_name).first.id, :value => new_cf_value}) end result = InsertCustomFieldJoins.find_or_create(nonprofit.id, [supporter.id], [ [ initial_custom_field_master.name, old_cf_value ], [ new_cf_name, new_cf_value ]]) expect(CustomFieldMaster.count).to eq 2 end end describe '.in_bulk' do context 'parameter validation' do it 'should validate parameters' do response = InsertCustomFieldJoins::in_bulk(nil, nil, nil) errors = response[:json][:errors] expect(errors.length).to eq(6) expect(response[:status]).to eq (:unprocessable_entity) expect_validation_errors(errors, [ {key: :np_id, name: :required}, {key: :np_id, name: :is_integer}, {key: :supporter_ids, name: :required}, {key: :supporter_ids, name: :is_array}, {key: :field_data, name: :is_array}, {key: :field_data, name: :required} ]) end context 'requiring db' do before(:each) { @nonprofit = force_create(:nonprofit) @bad_nonprofit = force_create(:nonprofit, :id => 50) } it 'nonprofit must be valid' do response = InsertCustomFieldJoins::in_bulk(@nonprofit.id+1, [], []) expect(response[:status]).to eq (:unprocessable_entity) expect(response[:json][:error]).to include("Nonprofit #{@nonprofit.id+1} is not valid") end it 'supporters if empty should do nothing' do response = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [], []) expect(response).to eq(successful_json(0, 0)) end it 'supporters if empty should do nothing' do response = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [50], []) expect(response).to eq(successful_json(0, 0)) end end end context 'main testing' do before(:each) { @nonprofit = force_create(:nonprofit) @random_supporter = create(:supporter) @other_nonprofit = force_create(:nonprofit) @delete_cfm= [20, 40, 60] @add_cfm = [25, 35] @supporters = { :np_supporter_with_add => { :cfm_ids => [65, 75, 85] }, :np_supporter_with_cfms_to_delete => { :cfm_ids => [40, 75, 85] }, :np_supporter_with_no_changes => { :cfm_ids => @add_cfm }, :np_supporter_with_some_of_both => { :cfm_ids => [20, 35] }, :supporter_from_other_np => { :cfm_ids => [100, 150, 200], :other_np => true } } @supporters.each_key {|k| i = @supporters[k] nonprofit_for_supporter =i[:other_np] ? @other_nonprofit : @nonprofit i[:entity] = create(:supporter, :nonprofit => nonprofit_for_supporter) i[:cfm_ids].each {|j| cfm = CustomFieldMaster.exists?(id: j) ? CustomFieldMaster.find(j) : create(:custom_field_master, id: j, nonprofit: nonprofit_for_supporter, name: "CFM #{j}") create(:custom_field_join, :value_from_id, :supporter_id => i[:entity].id, :custom_field_master => cfm) } } } it 'invalid nonprofit-supporter combo returns okay' do results = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [@supporters[:supporter_from_other_np][:entity].id], []) expect(results).to eq(successful_json(0, 0)) end it 'strips cfms which dont belong to nonprofit' do results = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [@supporters[:np_supporter_with_add][:entity].id], create_cfm_data([100], [150])) expect(results).to eq(successful_json(0, 0)) expect(CustomFieldJoin.where('supporter_id = ? and custom_field_master_id = ?', @supporters[:np_supporter_with_add][:entity].id, 100).count).to eq 0 end it 'delete' do expect(CustomFieldJoin.count).to eq 13 @supporters[:np_supporter_with_some_of_both][:entity].id results = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [@supporters[:np_supporter_with_some_of_both][:entity].id, @supporters[:np_supporter_with_cfms_to_delete][:entity].id, @supporters[:np_supporter_with_add][:entity].id, @supporters[:supporter_from_other_np][:entity].id, @supporters[:np_supporter_with_no_changes][:entity].id], create_cfm_data(@add_cfm, @delete_cfm)) expect(CustomFieldJoin.where("supporter_id = ? ", @supporters[:np_supporter_with_some_of_both][:entity].id).count).to eq 2 expect(CustomFieldJoin.where("supporter_id = ?", @supporters[:np_supporter_with_add][:entity].id).count).to eq 5 expect(CustomFieldJoin.where("supporter_id = ?", @supporters[:np_supporter_with_cfms_to_delete][:entity].id).count).to eq 4 expect(CustomFieldJoin.where("supporter_id = ?", @supporters[:supporter_from_other_np][:entity].id).count).to eq 3 expect(CustomFieldJoin.where("supporter_id = ?", @supporters[:np_supporter_with_no_changes][:entity].id).count).to eq 2 expect(CustomFieldJoin.count).to eq 16 end it 'id, updated_at, created_at changes are stripped' do invalid_id = 10000000 Timecop.freeze(2020, 9, 1, 12, 0, 0) { results = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [@supporters[:np_supporter_with_add][:entity].id], [{custom_field_master_id: 25, value: "CFM value 25", id: invalid_id, created_at: Time.now.ago(3000), updated_at: Time.now.ago(2999)}]) expected = {custom_field_master_id: 25, value: "CFM value 25", created_at: Time.now, updated_at: Time.now, supporter_id: @supporters[:np_supporter_with_add][:entity].id}.with_indifferent_access expect(results).to eq(successful_json(1, 0)) result_tag = @supporters[:np_supporter_with_add][:entity].custom_field_joins.where('custom_field_master_id = ?', 25).first expect(result_tag.attributes.with_indifferent_access.reject {|k, _| k == 'id'}).to eq(expected) expect(result_tag.attributes[:id]).to_not eq invalid_id } end it 'add_to_one' do expect(CustomFieldJoin.count).to eq 13 np_supporter_with_add_cfms = @supporters[:np_supporter_with_add][:entity].custom_field_joins.to_a np_supporter_with_some_of_both_cfms = @supporters[:np_supporter_with_some_of_both][:entity].custom_field_joins.to_a np_supporter_with_no_changes_cfms = @supporters[:np_supporter_with_no_changes][:entity].custom_field_joins.to_a Timecop.travel(20) { results = InsertCustomFieldJoins::in_bulk(@nonprofit.id, [ @supporters[:np_supporter_with_add][:entity].id, #add 2 @supporters[:np_supporter_with_no_changes][:entity], # update 2 @supporters[:np_supporter_with_some_of_both][:entity].id], #add 2, delete 1 create_cfm_data(@add_cfm, @delete_cfm)) expect(results).to eq(successful_json(6, 1)) expect(@supporters[:np_supporter_with_no_changes][:entity].custom_field_joins).to match_array(np_supporter_with_no_changes_cfms) expect(CustomFieldJoin.where("supporter_id = ? ", @supporters[:np_supporter_with_add][:entity].id).count).to eq 5 original_db_pairs = get_original_and_db(np_supporter_with_add_cfms, CustomFieldJoin.where("supporter_id = ? and custom_field_master_id in (?)", @supporters[:np_supporter_with_add][:entity].id, @supporters[:np_supporter_with_add][:cfm_ids]).pluck(:id)) original_db_pairs.each {|orig, db| expect(db.attributes.length).to eq(orig.attributes.length) expect(db.attributes).to eq(orig.attributes) } expect(CustomFieldJoin.where("supporter_id = ?", @supporters[:np_supporter_with_some_of_both][:entity].id).count).to eq 2 original_db_pairs = get_original_and_db(np_supporter_with_some_of_both_cfms, CustomFieldJoin.where("supporter_id = ? and custom_field_master_id in (?)", @supporters[:np_supporter_with_some_of_both][:entity].id, [35]).pluck(:id)) skip_attribs = ['updated_at', 'value'] original_db_pairs.each {|orig, db| expect(db.attributes.length).to eq(orig.attributes.length) expect(db.attributes.select {|key, value| !skip_attribs.include?(key)}).to eq(orig.attributes.select {|key, value| !skip_attribs.include?(key)}) expect(db.attributes['updated_at']).to be > orig.attributes['updated_at'] expect(db.attributes['value']).to eq "CFM value 35" } expect(CustomFieldJoin.count).to eq 15 } end end end def successful_json(inserted, deleted) {:json => {:inserted_count => inserted, :removed_count => deleted}, :status => :ok} end def create_cfm_data(cfm_to_add =[], cfm_to_delete=[]) use_nil = true cfm_to_add.map {|cfm| {custom_field_master_id: cfm, value: "CFM value #{cfm}"}} + cfm_to_delete.map {|cfm| value = use_nil ? nil : '' use_nil = !use_nil {custom_field_master_id: cfm, value: value}} end def get_original_and_db(original_items, ids_to_verify) ids_to_verify.map {|i| original_item = original_items.find {|oi| oi[:id] == i} db_item = CustomFieldJoin.find(i) [original_item, db_item] } end end