# frozen_string_literal: true # License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later # Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE require 'rails_helper' describe InsertRecurringDonation do describe '.with_stripe' do include_context :shared_rd_donation_value_context it 'does basic validation' do validation_basic_validation { InsertRecurringDonation.with_stripe(designation: 34_124, dedication: 35_141, event_id: 'bad', campaign_id: 'bad') } end it 'does recurring donation validation' do expect do InsertRecurringDonation.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid, recurring_donation: { interval: 'not number', start_date: 'not_date', time_unit: 4, paydate: 'faf' }) end .to raise_error { |e| expect(e).to be_a ParamValidation::ValidationError expect_validation_errors(e.data, [ { key: :interval, name: :is_integer }, { key: :start_date, name: :can_be_date }, { key: :time_unit, name: :included_in }, { key: :paydate, name: :is_integer } ]) } end it 'does paydate validation min' do expect do InsertRecurringDonation.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid, recurring_donation: { paydate: '0' }) end .to raise_error { |e| expect(e).to be_a ParamValidation::ValidationError expect_validation_errors(e.data, [ { key: :paydate, name: :min } ]) } end it 'does paydate validation max' do expect do InsertRecurringDonation.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid, recurring_donation: { paydate: '29' }) end .to raise_error { |e| expect(e).to be_a ParamValidation::ValidationError expect_validation_errors(e.data, [ { key: :paydate, name: :max } ]) } end it 'errors out if token is invalid' do validation_invalid_token { InsertRecurringDonation.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) } end it 'errors out if token is unauthorized' do validation_unauthorized { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) } end it 'errors out if token is expired' do validation_expired { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) } end describe 'errors during find if' do it 'supporter is invalid' do find_error_supporter { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: 55_555, token: source_token.token) } end it 'nonprofit is invalid' do find_error_nonprofit { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: 55_555, supporter_id: supporter.id, token: source_token.token) } end it 'campaign is invalid' do find_error_campaign { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: 5555) } end it 'event is invalid' do find_error_event { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: 5555) } end it 'profile is invalid' do find_error_profile { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: 5555) } end end describe 'errors during relationship comparison if' do it 'event is deleted' do validation_event_deleted { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id) } end it 'campaign is deleted' do validation_campaign_deleted { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id) } end it 'supporter is deleted' do validation_supporter_deleted { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token) } end it 'supporter doesnt belong to nonprofit' do validation_supporter_not_with_nonprofit { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: other_nonprofit_supporter.id, token: source_token.token) } end it 'campaign doesnt belong to nonprofit' do validation_campaign_not_with_nonprofit { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: other_campaign.id) } end it 'event doesnt belong to nonprofit' do validation_event_not_with_nonprofit { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: other_event.id) } end it 'card doesnt belong to supporter' do validation_card_not_with_supporter { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: other_source_token.token) } end end it 'charge returns failed' do handle_charge_failed { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token) } end describe 'success' do before(:each) do allow(SecureRandom).to receive(:uuid).and_return(default_edit_token) end describe 'charge happens' do before(:each) do before_each_success end it 'process event donation' do process_event_donation(recurring_donation: { paydate: nil, interval: 1, time_unit: 'year', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { time_unit: 'year' }) } end it 'process campaign donation' do expect(Houdini.event_publisher).to receive(:announce).with(:campaign_create, any_args) process_campaign_donation(recurring_donation: { paydate: nil, interval: 2, time_unit: 'month', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { interval: 2 }) } end it 'processes general donation with no recurring donation hash' do process_general_donation(recurring_donation: { paydate: Time.now.day, interval: 1, time_unit: 'month', start_date: Time.now.beginning_of_day }) do InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: Time.now.to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') end end end describe 'future charge' do before(:each) do before_each_success(false) end it 'processes general donation' do process_general_donation(expect_payment: false, expect_charge: false, recurring_donation: { paydate: (Time.now + 5.days).day, interval: 1, time_unit: 'month', start_date: (Time.now + 5.days).beginning_of_day }) do InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { start_date: (Time.now + 5.days).to_s }) end end end end end describe '.convert_donation_to_recurring_donation' do describe 'wonderful testing Eric' do before(:each) { Timecop.freeze(2020, 4, 29) } after(:each) { Timecop.return } let(:nonprofit) { force_create(:nm_justice, state_code_slug: 'wi', city_slug: 'city', slug: 'sluggster') } let(:profile) { force_create(:profile, user: force_create(:user)) } let(:supporter) { force_create(:supporter, nonprofit: nonprofit) } let(:card) { force_create(:card, holder: supporter) } let(:campaign) { force_create(:campaign, profile: profile, nonprofit: nonprofit) } let(:event) { force_create(:event, profile: profile, nonprofit: nonprofit) } let!(:donation) { force_create(:donation, nonprofit: nonprofit, supporter: supporter, amount: 4000, card: card, campaign: campaign, event: event) } let!(:payment) { force_create(:payment, donation: donation, kind: 'Donation') } it 'param validation' do expect { InsertRecurringDonation.convert_donation_to_recurring_donation(nil) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :donation_id, name: :required }, { key: :donation_id, name: :is_integer }]) end) end it 'rejects invalid donation' do expect { InsertRecurringDonation.convert_donation_to_recurring_donation(5555) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :donation_id }]) end) end it 'accepts proper information' do Timecop.freeze(2020, 5, 4) do rd = InsertRecurringDonation.convert_donation_to_recurring_donation(donation.id) # this needs some serious improvement expected_rd = { id: rd.id, donation_id: donation.id, nonprofit_id: nonprofit.id, supporter_id: supporter.id, updated_at: Time.now, created_at: Time.now, active: true, n_failures: 0, interval: 1, time_unit: 'month', start_date: donation.created_at.beginning_of_day, paydate: 28, profile_id: nil, cancelled_at: nil, cancelled_by: nil, amount: 4000, anonymous: nil, card_id: nil, campaign_id: nil, failure_message: nil, end_date: nil, email: nil, origin_url: nil }.with_indifferent_access expect(rd.attributes.except('edit_token')).to eq(expected_rd) expect(rd.edit_token).to_not be_falsey expect(rd.donation.recurring).to eq true expect(rd.donation.payment.kind).to eq 'RecurringDonation' end end end describe 'test for earlier in the month' do before(:each) { Timecop.freeze(2020, 4, 5) } after(:each) { Timecop.return } let(:nonprofit) { force_create(:nm_justice, state_code_slug: 'wi', city_slug: 'city', slug: 'sluggster') } let(:profile) { force_create(:profile, user: force_create(:user)) } let(:supporter) { force_create(:supporter, nonprofit: nonprofit) } let(:card) { force_create(:card, holder: supporter) } let(:campaign) { force_create(:campaign, profile: profile, nonprofit: nonprofit) } let(:event) { force_create(:event, profile: profile, nonprofit: nonprofit) } let!(:donation) { force_create(:donation, nonprofit: nonprofit, supporter: supporter, amount: 4000, card: card, campaign: campaign, event: event) } let!(:payment) { force_create(:payment, donation: donation, kind: 'Donation') } it 'works when the date is earlier in the month' do Timecop.freeze(2020, 4, 29) do rd = InsertRecurringDonation.convert_donation_to_recurring_donation(donation.id) # this needs some serious improvement expected_rd = { id: rd.id, donation_id: donation.id, nonprofit_id: nonprofit.id, supporter_id: supporter.id, updated_at: Time.now, created_at: Time.now, active: true, n_failures: 0, interval: 1, time_unit: 'month', start_date: donation.created_at.beginning_of_day, paydate: 5, profile_id: nil, cancelled_at: nil, cancelled_by: nil, amount: 4000, anonymous: nil, card_id: nil, campaign_id: nil, failure_message: nil, end_date: nil, email: nil, origin_url: nil }.with_indifferent_access expect(rd.attributes.except('edit_token')).to eq(expected_rd) expect(rd.donation.recurring).to eq true expect(rd.donation.payment.kind).to eq 'RecurringDonation' expect(rd.edit_token).to_not be_falsey end end end end end