<commit_before>require 'spec_helper'

module Spree
  describe Payment do
    describe "available actions" do
      context "for most gateways" do
        let(:payment) { create(:payment, source: create(:credit_card)) }

        it "can capture and void" do
          payment.actions.should match_array %w(capture void)
        end

        describe "when a payment has been taken" do
          before do
            payment.stub(:state) { 'completed' }
            payment.stub(:order) { double(:order, payment_state: 'credit_owed') }
          end

          it "can void and credit" do
            payment.actions.should match_array %w(void credit)
          end
        end
      end

      context "for Pin Payments" do
        let(:d) { create(:distributor_enterprise) }
        let(:pin) { Gateway::Pin.create! name: 'pin', distributor_ids: [d.id]}
        let(:payment) { create(:payment, source: create(:credit_card), payment_method: pin) }

        it "does not void" do
          payment.actions.should_not include 'void'
        end

        describe "when a payment has been taken" do
          before do
            payment.stub(:state) { 'completed' }
            payment.stub(:order) { double(:order, payment_state: 'credit_owed') }
          end

          it "can refund instead of crediting" do
            payment.actions.should_not include 'credit'
            payment.actions.should     include 'refund'
          end
        end
      end
    end

    describe "refunding" do
      let(:payment) { create(:payment) }
      let(:success) { double(:success? => true, authorization: 'abc123') }
      let(:failure) { double(:success? => false) }

      it "always checks the environment" do
        payment.payment_method.stub(:refund) { success }
        payment.should_receive(:check_environment)
        payment.refund!
      end

      describe "calculating refund amount" do
        it "returns the parameter amount when given" do
          payment.send(:calculate_refund_amount, 123).should === 123.0
        end

        it "refunds up to the value of the payment when the outstanding balance is larger" do
          payment.stub(:credit_allowed) { 123 }
          payment.stub(:order) { double(:order, outstanding_balance: 1000) }
          payment.send(:calculate_refund_amount).should == 123
        end

        it "refunds up to the outstanding balance of the order when the payment is larger" do
          payment.stub(:credit_allowed) { 1000 }
          payment.stub(:order) { double(:order, outstanding_balance: 123) }
          payment.send(:calculate_refund_amount).should == 123
        end
      end

      describe "performing refunds" do
        before do
          payment.stub(:calculate_refund_amount) { 123 }
          payment.payment_method.should_receive(:refund).and_return(success)
        end

        it "performs the refund without payment profiles" do
          payment.payment_method.stub(:payment_profiles_supported?) { false }
          payment.refund!
        end

        it "performs the refund with payment profiles" do
          payment.payment_method.stub(:payment_profiles_supported?) { true }
          payment.refund!
        end
      end

      it "records the response" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(success)
        payment.should_receive(:record_response).with(success)
        payment.refund!
      end

      it "records a payment on success" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(success)
        payment.stub(:record_response)

        expect do
          payment.refund!
        end.to change(Payment, :count).by(1)

        p = Payment.last
        p.order.should == payment.order
        p.source.should == payment
        p.payment_method.should == payment.payment_method
        p.amount.should == -123
        p.response_code.should == success.authorization
        p.state.should == 'completed'
      end

      it "logs the error on failure" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(failure)
        payment.stub(:record_response)
        payment.should_receive(:gateway_error).with(failure)
        payment.refund!
      end
    end

    describe "applying transaction fees" do
      let!(:order) { create(:order) }
      let!(:line_item) { create(:line_item, order: order, quantity: 3, price: 5.00) }

      before do
        order.reload.update!
      end

      context "to Stripe payments" do
        let(:shop) { create(:enterprise) }
        let(:payment_method) { create(:stripe_payment_method, distributor_ids: [create(:distributor_enterprise).id], preferred_enterprise_id: shop.id) }
        let(:payment) { create(:payment, order: order, payment_method: payment_method, amount: order.total) }
        let(:calculator) { Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }

        before do
          payment_method.calculator = calculator
          payment_method.save!

          allow(order).to receive(:pending_payments) { [payment] }
        end

        context "when the payment fails" do
          let(:failed_response) { ActiveMerchant::Billing::Response.new(false, "This is an error message") }

          before do
            allow(payment_method).to receive(:purchase) { failed_response }
          end

          it "makes the transaction fee ineligible and finalizes it" do
            # Decided to wrap the save process in order.process_payments!
            # since that is the context it is usually performed in
            order.process_payments!
            expect(order.payments.count).to eq 1
            expect(order.payments).to include payment<commit_msg>Add failing test for calculation of payment fee<commit_after>require 'spec_helper'

module Spree
  describe Payment do
    describe "available actions" do
      context "for most gateways" do
        let(:payment) { create(:payment, source: create(:credit_card)) }

        it "can capture and void" do
          payment.actions.should match_array %w(capture void)
        end

        describe "when a payment has been taken" do
          before do
            payment.stub(:state) { 'completed' }
            payment.stub(:order) { double(:order, payment_state: 'credit_owed') }
          end

          it "can void and credit" do
            payment.actions.should match_array %w(void credit)
          end
        end
      end

      context "for Pin Payments" do
        let(:d) { create(:distributor_enterprise) }
        let(:pin) { Gateway::Pin.create! name: 'pin', distributor_ids: [d.id]}
        let(:payment) { create(:payment, source: create(:credit_card), payment_method: pin) }

        it "does not void" do
          payment.actions.should_not include 'void'
        end

        describe "when a payment has been taken" do
          before do
            payment.stub(:state) { 'completed' }
            payment.stub(:order) { double(:order, payment_state: 'credit_owed') }
          end

          it "can refund instead of crediting" do
            payment.actions.should_not include 'credit'
            payment.actions.should     include 'refund'
          end
        end
      end
    end

    describe "refunding" do
      let(:payment) { create(:payment) }
      let(:success) { double(:success? => true, authorization: 'abc123') }
      let(:failure) { double(:success? => false) }

      it "always checks the environment" do
        payment.payment_method.stub(:refund) { success }
        payment.should_receive(:check_environment)
        payment.refund!
      end

      describe "calculating refund amount" do
        it "returns the parameter amount when given" do
          payment.send(:calculate_refund_amount, 123).should === 123.0
        end

        it "refunds up to the value of the payment when the outstanding balance is larger" do
          payment.stub(:credit_allowed) { 123 }
          payment.stub(:order) { double(:order, outstanding_balance: 1000) }
          payment.send(:calculate_refund_amount).should == 123
        end

        it "refunds up to the outstanding balance of the order when the payment is larger" do
          payment.stub(:credit_allowed) { 1000 }
          payment.stub(:order) { double(:order, outstanding_balance: 123) }
          payment.send(:calculate_refund_amount).should == 123
        end
      end

      describe "performing refunds" do
        before do
          payment.stub(:calculate_refund_amount) { 123 }
          payment.payment_method.should_receive(:refund).and_return(success)
        end

        it "performs the refund without payment profiles" do
          payment.payment_method.stub(:payment_profiles_supported?) { false }
          payment.refund!
        end

        it "performs the refund with payment profiles" do
          payment.payment_method.stub(:payment_profiles_supported?) { true }
          payment.refund!
        end
      end

      it "records the response" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(success)
        payment.should_receive(:record_response).with(success)
        payment.refund!
      end

      it "records a payment on success" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(success)
        payment.stub(:record_response)

        expect do
          payment.refund!
        end.to change(Payment, :count).by(1)

        p = Payment.last
        p.order.should == payment.order
        p.source.should == payment
        p.payment_method.should == payment.payment_method
        p.amount.should == -123
        p.response_code.should == success.authorization
        p.state.should == 'completed'
      end

      it "logs the error on failure" do
        payment.stub(:calculate_refund_amount) { 123 }
        payment.payment_method.stub(:refund).and_return(failure)
        payment.stub(:record_response)
        payment.should_receive(:gateway_error).with(failure)
        payment.refund!
      end
    end

    describe "applying transaction fees" do
      let!(:order) { create(:order) }
      let!(:line_item) { create(:line_item, order: order, quantity: 3, price: 5.00) }

      before do
        order.reload.update!
      end

      context "when order-based calculator" do
        let!(:shop) { create(:enterprise) }
        let!(:payment_method) { create(:payment_method, calculator: calculator) }

        let!(:calculator) do
          Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)
        end

        context "when order complete and inventory tracking enabled" do
          let!(:order) { create(:completed_order_with_totals, distributor: shop) }
          let!(:variant) { order.line_items.first.variant }
          let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant: variant) }

          it "creates adjustment" do
            payment = create(:payment, order: order, payment_method: payment_method,
                                       amount: order.total)
            expect(payment.adjustment).to be_present
            expect(payment.adjustment.amount).not_to eq(0)
          end
        end
      end

      context "to Stripe payments" do
        let(:shop) { create(:enterprise) }
        let(:payment_method) { create(:stripe_payment_method, distributor_ids: [create(:distributor_enterprise).id], preferred_enterprise_id: shop.id) }
        let(:payment) { create(:payment, order: order, payment_method: payment_method, amount: order.total) }
        let(:calculator) { Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }

        before do
          payment_method.calculator = calculator
          payment_method.save!

          allow(order).to receive(:pending_payments) { [payment] }
        end

        context "when the payment fails" do
          let(:failed_response) { ActiveMerchant::Billing::Response.new(false, "This is an error message") }

          before do
            allow(payment_method).to receive(:purchase) { failed_response }
          end

          it "makes the transaction fee ineligible and finalizes it" do
            # Decided to wrap the save process in order.process_payments!
            # since that is the context it is usually performed in
            order.process_payments!
            expect(order.payments.count).to eq 1
            expect(order.payments).to include payment
{'fraction_non_alphanumeric': 0.09549449679309525, 'fraction_numerical': 0.008630928814632987, 'mean_word_length': 2.4236920574681484, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4419997', 'n_tokens_mistral': 3576, 'n_tokens_neox': 3388, 'n_words': 890}