require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")

describe Braintree::Subscription do

  before(:each) do
    @credit_card = Braintree::Customer.create!(
      :credit_card => {
        :number => Braintree::Test::CreditCardNumbers::Visa,
        :expiration_date => "05/2010"
      },
    ).credit_cards[0]
  end

  describe "self.create" do
    it "is successful with a minimum of params" do
      result = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      )

      expect(result.success?).to eq(true)
      expect(result.subscription.id).to match(/^\w{6}$/)
      expect(result.subscription.status).to eq(Braintree::Subscription::Status::Active)
      expect(result.subscription.plan_id).to eq("integration_trialless_plan")

      expect(result.subscription.first_billing_date).to be_a Date
      expect(result.subscription.next_billing_date).to be_a Date
      expect(result.subscription.billing_period_start_date).to be_a Date
      expect(result.subscription.billing_period_end_date).to be_a Date
      expect(result.subscription.paid_through_date).to be_a Date

      expect(result.subscription.created_at.between?(Time.now - 60, Time.now)).to eq(true)
      expect(result.subscription.updated_at.between?(Time.now - 60, Time.now)).to eq(true)

      expect(result.subscription.failure_count).to eq(0)
      expect(result.subscription.next_billing_period_amount).to eq("12.34")
      expect(result.subscription.payment_method_token).to eq(@credit_card.token)

      expect(result.subscription.status_history.first.price).to eq("12.34")
      expect(result.subscription.status_history.first.status).to eq(Braintree::Subscription::Status::Active)
      expect(result.subscription.status_history.first.subscription_source).to eq(Braintree::Subscription::Source::Api)
      expect(result.subscription.status_history.first.currency_iso_code).to eq("USD")
      expect(result.subscription.status_history.first.plan_id).to eq(SpecHelper::TriallessPlan[:id])
    end

    it "returns a transaction with billing period populated" do
      result = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      )

      expect(result.success?).to eq(true)
      subscription = result.subscription
      transaction = subscription.transactions.first

      expect(transaction.subscription_details.billing_period_start_date).to eq(subscription.billing_period_start_date)
      expect(transaction.subscription_details.billing_period_end_date).to eq(subscription.billing_period_end_date)
    end

    it "can set the id" do
      new_id = rand(36**9).to_s(36)
      result = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TrialPlan[:id],
        :id => new_id,
      )

      expect(result.success?).to eq(true)
      expect(result.subscription.id).to eq(new_id)
    end

    context "with payment_method_nonces" do
      it "creates a subscription when given a credit card payment_method_nonce" do
        nonce = nonce_for_new_payment_method(
          :credit_card => {
            :number => Braintree::Test::CreditCardNumbers::Visa,
            :expiration_month => "11",
            :expiration_year => "2099",
          },
          :client_token_options => {
            :customer_id => @credit_card.customer_id
          },
        )
        result = Braintree::Subscription.create(
          :payment_method_nonce => nonce,
          :plan_id => SpecHelper::TriallessPlan[:id],
        )

        expect(result.success?).to eq(true)
        transaction = result.subscription.transactions[0]
        expect(transaction.credit_card_details.bin).to eq(Braintree::Test::CreditCardNumbers::Visa[0, 6])
        expect(transaction.credit_card_details.last_4).to eq(Braintree::Test::CreditCardNumbers::Visa[-4, 4])
      end

      it "creates a subscription when given a paypal account payment_method_nonce" do
        customer = Braintree::Customer.create!
        payment_method_result = Braintree::PaymentMethod.create(
          :payment_method_nonce => Braintree::Test::Nonce::PayPalBillingAgreement,
          :customer_id => customer.id,
        )

        result = Braintree::Subscription.create(
          :payment_method_token => payment_method_result.payment_method.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
        )

        expect(result).to be_success
        transaction = result.subscription.transactions[0]
        expect(transaction.paypal_details.payer_email).to eq("payer@example.com")
      end

      it "creates a subscription when given a paypal description" do
        customer = Braintree::Customer.create!
        payment_method_result = Braintree::PaymentMethod.create(
          :payment_method_nonce => Braintree::Test::Nonce::PayPalFuturePayment,
          :customer_id => customer.id,
        )

        result = Braintree::Subscription.create(
          :payment_method_token => payment_method_result.payment_method.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :options => {
            :paypal => {
              :description => "A great product",
            },
          },
        )

        expect(result).to be_success
        subscription = result.subscription
        expect(subscription.description).to eq("A great product")
        transaction = subscription.transactions[0]
        expect(transaction.paypal_details.payer_email).to eq("payer@example.com")
        expect(transaction.paypal_details.description).to eq("A great product")
      end

      it "returns an error if the payment_method_nonce hasn't been vaulted" do
        result = Braintree::Subscription.create(
          :payment_method_nonce => Braintree::Test::Nonce::PayPalFuturePayment,
          :plan_id => SpecHelper::TriallessPlan[:id],
        )

        expect(result).not_to be_success
        expect(result.errors.for(:subscription).on(:payment_method_nonce).first.code).to eq("91925")
      end
    end

    context "billing_day_of_month" do
      it "inherits from the plan if not provided" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::BillingDayOfMonthPlan[:id],
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.billing_day_of_month).to eq(5)
      end

      it "allows overriding" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::BillingDayOfMonthPlan[:id],
          :billing_day_of_month => 25,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.billing_day_of_month).to eq(25)
      end

      it "allows overriding with start_immediately" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::BillingDayOfMonthPlan[:id],
          :options => {
            :start_immediately => true
          },
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.transactions.size).to eq(1)
      end
    end

    context "first_billing_date" do
      it "allows specifying" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::BillingDayOfMonthPlan[:id],
          :first_billing_date => Date.today + 3,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.first_billing_date).to eq(Date.today + 3)
        expect(result.subscription.status).to eq(Braintree::Subscription::Status::Pending)
      end

      it "returns an error if the date is in the past" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::BillingDayOfMonthPlan[:id],
          :first_billing_date => Date.today - 3,
        )

        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:first_billing_date).first.code).to eq(Braintree::ErrorCodes::Subscription::FirstBillingDateCannotBeInThePast)
      end
    end

    context "merchant_account_id" do
      it "defaults to the default merchant account if no merchant_account_id is provided" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.merchant_account_id).to eq(SpecHelper::DefaultMerchantAccountId)
      end

      it "allows setting the merchant_account_id" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.merchant_account_id).to eq(SpecHelper::NonDefaultMerchantAccountId)
      end
    end

    context "number_of_billing_cycles" do
      it "sets the number of billing cycles on the subscription when provided" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :number_of_billing_cycles => 10,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.number_of_billing_cycles).to eq(10)
      end

      it "sets the number of billing cycles to nil if :never_expires => true" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :never_expires => true,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.number_of_billing_cycles).to eq(nil)
      end
    end

    context "trial period" do
      context "defaults to the plan's trial period settings" do
        it "with no trial" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TriallessPlan[:id],
          )

          expect(result.subscription.trial_period).to eq(false)
          expect(result.subscription.trial_duration).to eq(nil)
          expect(result.subscription.trial_duration_unit).to eq(nil)
        end

        it "with a trial" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
          )

          expect(result.subscription.trial_period).to eq(true)
          expect(result.subscription.trial_duration).to eq(2)
          expect(result.subscription.trial_duration_unit).to eq(Braintree::Subscription::TrialDurationUnit::Day)
        end

        it "can alter the trial period params" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
            :trial_duration => 5,
            :trial_duration_unit => Braintree::Subscription::TrialDurationUnit::Month,
          )

          expect(result.subscription.trial_period).to eq(true)
          expect(result.subscription.trial_duration).to eq(5)
          expect(result.subscription.trial_duration_unit).to eq(Braintree::Subscription::TrialDurationUnit::Month)
        end

        it "can override the trial_period param" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
            :trial_period => false,
          )

          expect(result.subscription.trial_period).to eq(false)
        end

        it "doesn't create a transaction if there's a trial period" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
          )

          expect(result.subscription.transactions.size).to eq(0)
        end
      end

      context "no trial period" do
        it "creates a transaction if no trial period" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TriallessPlan[:id],
          )

          expect(result.subscription.transactions.size).to eq(1)
          expect(result.subscription.transactions.first).to be_a(Braintree::Transaction)
          expect(result.subscription.transactions.first.amount).to eq(SpecHelper::TriallessPlan[:price])
          expect(result.subscription.transactions.first.type).to eq(Braintree::Transaction::Type::Sale)
          expect(result.subscription.transactions.first.subscription_id).to eq(result.subscription.id)
        end

        it "does not create the subscription and returns the transaction if the transaction is not successful" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TriallessPlan[:id],
            :price => Braintree::Test::TransactionAmounts::Decline,
          )

          expect(result.success?).to be(false)
          expect(result.transaction.status).to eq(Braintree::Transaction::Status::ProcessorDeclined)
          expect(result.message).to eq("Do Not Honor")
        end
      end

      context "price" do
        it "defaults to the plan's price" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
          )

          expect(result.subscription.price).to eq(SpecHelper::TrialPlan[:price])
        end

        it "can be overridden" do
          result = Braintree::Subscription.create(
            :payment_method_token => @credit_card.token,
            :plan_id => SpecHelper::TrialPlan[:id],
            :price => 98.76,
          )

          expect(result.subscription.price).to eq(BigDecimal("98.76"))
        end
      end
    end

    context "validation errors" do
      it "has validation errors on id" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => "invalid token",
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:id)[0].message).to eq("ID is invalid (use only letters, numbers, '-', and '_').")
      end

      it "has validation errors on duplicate id" do
        duplicate_token = "duplicate_token_#{rand(36**8).to_s(36)}"
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => duplicate_token,
        )
        expect(result.success?).to eq(true)

        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => duplicate_token,
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:id)[0].message).to eq("ID has already been taken.")
      end

      it "trial duration required" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :trial_period => true,
          :trial_duration => nil,
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:trial_duration)[0].message).to eq("Trial Duration is required.")
      end

      it "trial duration unit required" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :trial_period => true,
          :trial_duration => 2,
          :trial_duration_unit => nil,
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:trial_duration_unit)[0].message).to eq("Trial Duration Unit is invalid.")
      end
    end

    context "add_ons and discounts" do
      it "does not inherit the add_ons and discounts from the plan when do_not_inherit_add_ons_or_discounts is set" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
          :options => {:do_not_inherit_add_ons_or_discounts => true},
        )
        expect(result.success?).to eq(true)

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(0)
        expect(subscription.discounts.size).to eq(0)
      end

      it "inherits the add_ons and discounts from the plan when not specified" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
        )
        expect(result.success?).to eq(true)

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(2)
        add_ons = subscription.add_ons.sort_by { |add_on| add_on.id }

        expect(add_ons.first.id).to eq("increase_10")
        expect(add_ons.first.amount).to eq(BigDecimal("10.00"))
        expect(add_ons.first.quantity).to eq(1)
        expect(add_ons.first.number_of_billing_cycles).to be_nil
        expect(add_ons.first.never_expires?).to be(true)
        expect(add_ons.first.current_billing_cycle).to eq(0)

        expect(add_ons.last.id).to eq("increase_20")
        expect(add_ons.last.amount).to eq(BigDecimal("20.00"))
        expect(add_ons.last.quantity).to eq(1)
        expect(add_ons.last.number_of_billing_cycles).to be_nil
        expect(add_ons.last.never_expires?).to be(true)
        expect(add_ons.last.current_billing_cycle).to eq(0)

        expect(subscription.discounts.size).to eq(2)
        discounts = subscription.discounts.sort_by { |discount| discount.id }

        expect(discounts.first.id).to eq("discount_11")
        expect(discounts.first.amount).to eq(BigDecimal("11.00"))
        expect(discounts.first.quantity).to eq(1)
        expect(discounts.first.number_of_billing_cycles).to be_nil
        expect(discounts.first.never_expires?).to be(true)
        expect(discounts.first.current_billing_cycle).to eq(0)

        expect(discounts.last.id).to eq("discount_7")
        expect(discounts.last.amount).to eq(BigDecimal("7.00"))
        expect(discounts.last.quantity).to eq(1)
        expect(discounts.last.number_of_billing_cycles).to be_nil
        expect(discounts.last.never_expires?).to be(true)
        expect(discounts.last.current_billing_cycle).to eq(0)
      end

      it "allows overriding of inherited add_ons and discounts" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
          :add_ons => {
            :update => [
              {
                :amount => BigDecimal("50.00"),
                :existing_id => SpecHelper::AddOnIncrease10,
                :quantity => 2,
                :number_of_billing_cycles => 5
              }
            ]
          },
          :discounts => {
            :update => [
              {
                :amount => BigDecimal("15.00"),
                :existing_id => SpecHelper::Discount7,
                :quantity => 3,
                :never_expires => true
              }
            ]
          },
        )
        expect(result.success?).to eq(true)

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(2)
        add_ons = subscription.add_ons.sort_by { |add_on| add_on.id }

        expect(add_ons.first.id).to eq("increase_10")
        expect(add_ons.first.amount).to eq(BigDecimal("50.00"))
        expect(add_ons.first.quantity).to eq(2)
        expect(add_ons.first.number_of_billing_cycles).to eq(5)
        expect(add_ons.first.never_expires?).to be(false)
        expect(add_ons.first.current_billing_cycle).to eq(0)

        expect(add_ons.last.id).to eq("increase_20")
        expect(add_ons.last.amount).to eq(BigDecimal("20.00"))
        expect(add_ons.last.quantity).to eq(1)
        expect(add_ons.last.current_billing_cycle).to eq(0)

        expect(subscription.discounts.size).to eq(2)
        discounts = subscription.discounts.sort_by { |discount| discount.id }

        expect(discounts.first.id).to eq("discount_11")
        expect(discounts.first.amount).to eq(BigDecimal("11.00"))
        expect(discounts.first.quantity).to eq(1)
        expect(discounts.first.current_billing_cycle).to eq(0)

        expect(discounts.last.id).to eq("discount_7")
        expect(discounts.last.amount).to eq(BigDecimal("15.00"))
        expect(discounts.last.quantity).to eq(3)
        expect(discounts.last.number_of_billing_cycles).to be_nil
        expect(discounts.last.never_expires?).to be(true)
        expect(discounts.last.current_billing_cycle).to eq(0)
      end

      it "allows deleting of inherited add_ons and discounts" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
          :add_ons => {
            :remove => [SpecHelper::AddOnIncrease10]
          },
          :discounts => {
            :remove => [SpecHelper::Discount7]
          },
        )
        expect(result.success?).to eq(true)

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(1)
        expect(subscription.add_ons.first.id).to eq("increase_20")
        expect(subscription.add_ons.first.amount).to eq(BigDecimal("20.00"))
        expect(subscription.add_ons.first.quantity).to eq(1)
        expect(subscription.add_ons.first.current_billing_cycle).to eq(0)

        expect(subscription.discounts.size).to eq(1)
        expect(subscription.discounts.last.id).to eq("discount_11")
        expect(subscription.discounts.last.amount).to eq(BigDecimal("11.00"))
        expect(subscription.discounts.last.quantity).to eq(1)
        expect(subscription.discounts.last.current_billing_cycle).to eq(0)
      end

      it "allows adding new add_ons and discounts" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
          :add_ons => {
            :add => [{:inherited_from_id => SpecHelper::AddOnIncrease30}]
          },
          :discounts => {
            :add => [{:inherited_from_id => SpecHelper::Discount15}]
          },
        )
        expect(result.success?).to eq(true)
        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(3)
        add_ons = subscription.add_ons.sort_by { |add_on| add_on.id }

        expect(add_ons[0].id).to eq("increase_10")
        expect(add_ons[0].amount).to eq(BigDecimal("10.00"))
        expect(add_ons[0].quantity).to eq(1)

        expect(add_ons[1].id).to eq("increase_20")
        expect(add_ons[1].amount).to eq(BigDecimal("20.00"))
        expect(add_ons[1].quantity).to eq(1)

        expect(add_ons[2].id).to eq("increase_30")
        expect(add_ons[2].amount).to eq(BigDecimal("30.00"))
        expect(add_ons[2].quantity).to eq(1)

        expect(subscription.discounts.size).to eq(3)
        discounts = subscription.discounts.sort_by { |discount| discount.id }

        expect(discounts[0].id).to eq("discount_11")
        expect(discounts[0].amount).to eq(BigDecimal("11.00"))
        expect(discounts[0].quantity).to eq(1)

        expect(discounts[1].id).to eq("discount_15")
        expect(discounts[1].amount).to eq(BigDecimal("15.00"))
        expect(discounts[1].quantity).to eq(1)

        expect(discounts[2].id).to eq("discount_7")
        expect(discounts[2].amount).to eq(BigDecimal("7.00"))
        expect(discounts[2].quantity).to eq(1)
      end

      it "properly parses validation errors for arrays" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
          :add_ons => {
            :update => [
              {
                :existing_id => SpecHelper::AddOnIncrease10,
                :amount => "invalid"
              },
              {
                :existing_id => SpecHelper::AddOnIncrease20,
                :quantity => -10,
              }
            ]
          },
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).for(:add_ons).for(:update).for_index(0).on(:amount)[0].code).to eq(Braintree::ErrorCodes::Subscription::Modification::AmountIsInvalid)
        expect(result.errors.for(:subscription).for(:add_ons).for(:update).for_index(1).on(:quantity)[0].code).to eq(Braintree::ErrorCodes::Subscription::Modification::QuantityIsInvalid)
      end
    end

    context "descriptors" do
      it "accepts name and phone and copies them to the transaction" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :descriptor => {
            :name => "123*123456789012345678",
            :phone => "3334445555"
          },
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.descriptor.name).to eq("123*123456789012345678")
        expect(result.subscription.descriptor.phone).to eq("3334445555")

        expect(result.subscription.transactions.size).to eq(1)
        transaction = result.subscription.transactions.first
        expect(transaction.descriptor.name).to eq("123*123456789012345678")
        expect(transaction.descriptor.phone).to eq("3334445555")
      end

      it "has validation errors if format is invalid" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :descriptor => {
            :name => "badcompanyname12*badproduct12",
            :phone => "%bad4445555",
            :url => "12345678901234"
          },
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).for(:descriptor).on(:name)[0].code).to eq(Braintree::ErrorCodes::Descriptor::NameFormatIsInvalid)
        expect(result.errors.for(:subscription).for(:descriptor).on(:phone)[0].code).to eq(Braintree::ErrorCodes::Descriptor::PhoneFormatIsInvalid)
        expect(result.errors.for(:subscription).for(:descriptor).on(:url)[0].code).to eq(Braintree::ErrorCodes::Descriptor::UrlFormatIsInvalid)
      end
    end
  end

  describe "self.create!" do
    it "returns the subscription if valid" do
      subscription = Braintree::Subscription.create!(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      )

      expect(subscription.id).to match(/^\w{6}$/)
      expect(subscription.status).to eq(Braintree::Subscription::Status::Active)
      expect(subscription.plan_id).to eq("integration_trialless_plan")

      expect(subscription.first_billing_date).to be_a Date
      expect(subscription.next_billing_date).to be_a Date
      expect(subscription.billing_period_start_date).to be_a Date
      expect(subscription.billing_period_end_date).to be_a Date
      expect(subscription.paid_through_date).to be_a Date

      expect(subscription.failure_count).to eq(0)
      expect(subscription.current_billing_cycle).to eq(1)
      expect(subscription.next_billing_period_amount).to eq("12.34")
      expect(subscription.payment_method_token).to eq(@credit_card.token)
    end

    it "raises a ValidationsFailed if invalid" do
      expect do
        Braintree::Subscription.create!(
          :payment_method_token => @credit_card.token,
          :plan_id => "not_a_plan_id",
        )
      end.to raise_error(Braintree::ValidationsFailed)
    end
  end

  describe "self.find" do
    it "finds a subscription" do
      result = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TrialPlan[:id],
      )
      expect(result.success?).to eq(true)

      expect(Braintree::Subscription.find(result.subscription.id)).to eq(result.subscription)
    end

    it "raises Braintree::NotFoundError if it cannot find" do
      expect {
        Braintree::Subscription.find("noSuchSubscription")
      }.to raise_error(Braintree::NotFoundError, 'subscription with id "noSuchSubscription" not found')
    end
  end

  describe "self.update" do
    before(:each) do
      @subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :price => 54.32,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
    end

    it "allows changing the merchant_account_id" do
      result = Braintree::Subscription.update(@subscription.id,
        :merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
      )

      expect(result.success?).to eq(true)
      expect(result.subscription.merchant_account_id).to eq(SpecHelper::NonDefaultMerchantAccountId)
    end

    it "allows changing the payment method by payment_method_token" do
      new_credit_card = Braintree::CreditCard.create!(
        :customer_id => @credit_card.customer_id,
        :number => Braintree::Test::CreditCardNumbers::Visa,
        :expiration_date => "05/2010",
      )

      result = Braintree::Subscription.update(@subscription.id,
        :payment_method_token => new_credit_card.token,
      )

      expect(result.subscription.payment_method_token).to eq(new_credit_card.token)
    end

    it "allows changing the payment_method by payment_method_nonce" do
      nonce = nonce_for_new_payment_method(
        :credit_card => {
          :number => Braintree::Test::CreditCardNumbers::MasterCard,
          :expiration_date => "05/2010"
        },
        :client_token_options => {
          :customer_id => @credit_card.customer_id,
        },
      )

      result = Braintree::Subscription.update(@subscription.id, :payment_method_nonce => nonce)
      expect(result.subscription.transactions[0].credit_card_details.token).to eq(@credit_card.token)
      expect(result.subscription.payment_method_token).not_to eq(@credit_card.token)
    end

    it "allows changing the descriptors" do
      result = Braintree::Subscription.update(@subscription.id,
        :descriptor => {
          :name => "aaa*1234",
          :phone => "3334443333",
          :url => "ebay.com"
        },
      )

      expect(result.success?).to eq(true)
      expect(result.subscription.descriptor.name).to eq("aaa*1234")
      expect(result.subscription.descriptor.phone).to eq("3334443333")
      expect(result.subscription.descriptor.url).to eq("ebay.com")
    end

    it "allows changing the paypal description" do
      customer = Braintree::Customer.create!
      payment_method = Braintree::PaymentMethod.create(
        :payment_method_nonce => Braintree::Test::Nonce::PayPalFuturePayment,
        :customer_id => customer.id,
      ).payment_method

      subscription = Braintree::Subscription.create(
        :payment_method_token => payment_method.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription

      result = Braintree::Subscription.update(
        subscription.id,
        :options => {
          :paypal => {
            :description => "A great product",
          },
        },
      )

      expect(result.success?).to eq(true)
      expect(result.subscription.description).to eq("A great product")
    end

    context "when successful" do
      it "returns a success response with the updated subscription if valid" do
        new_id = rand(36**9).to_s(36)
        result = Braintree::Subscription.update(@subscription.id,
          :id => new_id,
          :price => 9999.88,
          :plan_id => SpecHelper::TrialPlan[:id],
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.id).to match(/#{new_id}/)
        expect(result.subscription.plan_id).to eq(SpecHelper::TrialPlan[:id])
        expect(result.subscription.price).to eq(BigDecimal("9999.88"))
      end

      context "proration" do
        it "prorates if there is a charge (because merchant has proration option enabled in control panel)" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f + 1,
          )

          expect(result.success?).to eq(true)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f + 1)
          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size + 1)
        end

        it "allows the user to force proration if there is a charge" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f + 1,
            :options => {:prorate_charges => true},
          )

          expect(result.success?).to eq(true)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f + 1)
          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size + 1)
        end

        it "allows the user to prevent proration if there is a charge" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f + 1,
            :options => {:prorate_charges => false},
          )

          expect(result.success?).to eq(true)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f + 1)
          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size)
        end

        it "doesn't prorate if price decreases" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f - 1,
          )

          expect(result.success?).to eq(true)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f - 1)
          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size)
        end

        it "updates the subscription if the proration fails and revert_subscription_on_proration_failure => false" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f + 2100,
            :options => {
              :revert_subscription_on_proration_failure => false
            },
          )

          expect(result.success?).to eq(true)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f + 2100)

          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size + 1)
          transaction = result.subscription.transactions.first
          expect(transaction.status).to eq(Braintree::Transaction::Status::ProcessorDeclined)
          expect(result.subscription.balance).to eq(transaction.amount)
        end

        it "does not update the subscription if the proration fails and revert_subscription_on_proration_failure => true" do
          result = Braintree::Subscription.update(@subscription.id,
            :price => @subscription.price.to_f + 2100,
            :options => {
              :revert_subscription_on_proration_failure => true
            },
          )

          expect(result.success?).to eq(false)
          expect(result.subscription.price.to_f).to eq(@subscription.price.to_f)

          expect(result.subscription.transactions.size).to eq(@subscription.transactions.size + 1)
          transaction = result.subscription.transactions.first
          expect(transaction.status).to eq(Braintree::Transaction::Status::ProcessorDeclined)
          expect(result.subscription.balance).to eq(0)
        end
      end
    end

    context "when unsuccessful" do
      before(:each) do
        @subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
        ).subscription
      end

      it "raises NotFoundError if the subscription can't be found" do
        expect {
          Braintree::Subscription.update(rand(36**9).to_s(36),
            :price => 58.20,
          )
        }.to raise_error(Braintree::NotFoundError)
      end

      it "has validation errors on id" do
        result = Braintree::Subscription.update(@subscription.id, :id => "invalid token")
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:id)[0].code).to eq(Braintree::ErrorCodes::Subscription::TokenFormatIsInvalid)
      end

      it "has a price" do
        result = Braintree::Subscription.update(@subscription.id, :price => "")
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:price)[0].code).to eq(Braintree::ErrorCodes::Subscription::PriceCannotBeBlank)
      end

      it "has a properly formatted price" do
        result = Braintree::Subscription.update(@subscription.id, :price => "9.2.1 apples")
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription).on(:price)[0].code).to eq(Braintree::ErrorCodes::Subscription::PriceFormatIsInvalid)
      end

      it "has validation errors on duplicate id" do
        duplicate_id = "new_id_#{rand(36**6).to_s(36)}"
        Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => duplicate_id,
        )
        duplicate = Braintree::Subscription.update(
          @subscription.id,
          :id => duplicate_id,
        )
        expect(duplicate.success?).to eq(false)
        expect(duplicate.errors.for(:subscription).on(:id)[0].code).to eq(Braintree::ErrorCodes::Subscription::IdIsInUse)
      end

      it "cannot update a canceled subscription" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :price => 54.32,
          :plan_id => SpecHelper::TriallessPlan[:id],
        ).subscription

        result = Braintree::Subscription.cancel(subscription.id)
        expect(result.success?).to eq(true)

        result = Braintree::Subscription.update(subscription.id,
          :price => 123.45,
        )
        expect(result.success?).to eq(false)
        expect(result.errors.for(:subscription)[0].code).to eq(Braintree::ErrorCodes::Subscription::CannotEditCanceledSubscription)
      end
    end

    context "number_of_billing_cycles" do
      it "sets the number of billing cycles on the subscription when provided" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :number_of_billing_cycles => 10,
        ).subscription

        result = Braintree::Subscription.update(
          subscription.id,
          :number_of_billing_cycles => 5,
        )

        expect(result.subscription.number_of_billing_cycles).to eq(5)
      end

      it "sets the number of billing cycles to nil if :never_expires => true" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :number_of_billing_cycles => 10,
        ).subscription

        result = Braintree::Subscription.update(
          subscription.id,
          :never_expires => true,
        )

        expect(result.success?).to eq(true)
        expect(result.subscription.number_of_billing_cycles).to eq(nil)
        expect(result.subscription.never_expires?).to be(true)
      end
    end

    context "add_ons and discounts" do
      it "can update add_ons and discounts" do
        result = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
        )
        expect(result.success?).to eq(true)
        subscription = result.subscription

        result = Braintree::Subscription.update(
          subscription.id,
          :add_ons => {
            :update => [
              {
                :existing_id => subscription.add_ons.first.id,
                :amount => BigDecimal("99.99"),
                :quantity => 12
              }
            ]
          },
          :discounts => {
            :update => [
              {
                :existing_id => subscription.discounts.first.id,
                :amount => BigDecimal("88.88"),
                :quantity => 9
              }
            ]
          },
        )

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(2)
        add_ons = subscription.add_ons.sort_by { |add_on| add_on.id }

        expect(add_ons.first.amount).to eq(BigDecimal("99.99"))
        expect(add_ons.first.quantity).to eq(12)

        expect(subscription.discounts.size).to eq(2)
        discounts = subscription.discounts.sort_by { |discount| discount.id }

        expect(discounts.last.amount).to eq(BigDecimal("88.88"))
        expect(discounts.last.quantity).to eq(9)
      end

      it "allows adding new add_ons and discounts" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
        ).subscription

        result = Braintree::Subscription.update(subscription.id,
          :add_ons => {
            :add => [{:inherited_from_id => SpecHelper::AddOnIncrease30}]
          },
          :discounts => {
            :add => [{:inherited_from_id => SpecHelper::Discount15}]
          },
        )

        expect(result.success?).to eq(true)
        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(3)
        add_ons = subscription.add_ons.sort_by { |add_on| add_on.id }

        expect(add_ons[0].id).to eq("increase_10")
        expect(add_ons[0].amount).to eq(BigDecimal("10.00"))
        expect(add_ons[0].quantity).to eq(1)

        expect(add_ons[1].id).to eq("increase_20")
        expect(add_ons[1].amount).to eq(BigDecimal("20.00"))
        expect(add_ons[1].quantity).to eq(1)

        expect(add_ons[2].id).to eq("increase_30")
        expect(add_ons[2].amount).to eq(BigDecimal("30.00"))
        expect(add_ons[2].quantity).to eq(1)

        expect(subscription.discounts.size).to eq(3)
        discounts = subscription.discounts.sort_by { |discount| discount.id }

        expect(discounts[0].id).to eq("discount_11")
        expect(discounts[0].amount).to eq(BigDecimal("11.00"))
        expect(discounts[0].quantity).to eq(1)

        expect(discounts[1].id).to eq("discount_15")
        expect(discounts[1].amount).to eq(BigDecimal("15.00"))
        expect(discounts[1].quantity).to eq(1)

        expect(discounts[2].id).to eq("discount_7")
        expect(discounts[2].amount).to eq(BigDecimal("7.00"))
        expect(discounts[2].quantity).to eq(1)
      end

      it "allows replacing entire set of add_ons and discounts" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
        ).subscription

        result = Braintree::Subscription.update(subscription.id,
          :add_ons => {
            :add => [{:inherited_from_id => SpecHelper::AddOnIncrease30}]
          },
          :discounts => {
            :add => [{:inherited_from_id => SpecHelper::Discount15}]
          },
          :options => {:replace_all_add_ons_and_discounts => true},
        )

        expect(result.success?).to eq(true)
        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(1)

        expect(subscription.add_ons[0].amount).to eq(BigDecimal("30.00"))
        expect(subscription.add_ons[0].quantity).to eq(1)

        expect(subscription.discounts.size).to eq(1)

        expect(subscription.discounts[0].amount).to eq(BigDecimal("15.00"))
        expect(subscription.discounts[0].quantity).to eq(1)
      end

      it "allows deleting of add_ons and discounts" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::AddOnDiscountPlan[:id],
        ).subscription

        result = Braintree::Subscription.update(subscription.id,
          :add_ons => {
            :remove => [SpecHelper::AddOnIncrease10]
          },
          :discounts => {
            :remove => [SpecHelper::Discount7]
          },
        )
        expect(result.success?).to eq(true)

        subscription = result.subscription

        expect(subscription.add_ons.size).to eq(1)
        expect(subscription.add_ons.first.amount).to eq(BigDecimal("20.00"))
        expect(subscription.add_ons.first.quantity).to eq(1)

        expect(subscription.discounts.size).to eq(1)
        expect(subscription.discounts.last.amount).to eq(BigDecimal("11.00"))
        expect(subscription.discounts.last.quantity).to eq(1)
      end
    end
  end

  describe "self.update!" do
    before(:each) do
      @subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :price => 54.32,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
    end

    it "returns the updated subscription if valid" do
      new_id = rand(36**9).to_s(36)
      subscription = Braintree::Subscription.update!(@subscription.id,
        :id => new_id,
        :price => 9999.88,
        :plan_id => SpecHelper::TrialPlan[:id],
      )

      expect(subscription.id).to match(/#{new_id}/)
      expect(subscription.plan_id).to eq(SpecHelper::TrialPlan[:id])
      expect(subscription.price).to eq(BigDecimal("9999.88"))
    end

    it "raises a ValidationsFailed if invalid" do
      expect do
        Braintree::Subscription.update!(@subscription.id,
          :plan_id => "not_a_plan_id",
        )
      end.to raise_error(Braintree::ValidationsFailed)
    end

  end

  describe "self.cancel" do
    it "returns a success response with the updated subscription if valid" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :price => 54.32,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription

      result = Braintree::Subscription.cancel(subscription.id)
      expect(result.success?).to eq(true)
      expect(result.subscription.status).to eq(Braintree::Subscription::Status::Canceled)
    end

    it "returns a validation error if record not found" do
      expect {
        Braintree::Subscription.cancel("noSuchSubscription")
      }.to raise_error(Braintree::NotFoundError, 'subscription with id "noSuchSubscription" not found')
    end

    it "cannot be canceled if already canceled" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :price => 54.32,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription

      result = Braintree::Subscription.cancel(subscription.id)
      expect(result.success?).to eq(true)
      expect(result.subscription.status).to eq(Braintree::Subscription::Status::Canceled)

      result = Braintree::Subscription.cancel(subscription.id)
      expect(result.success?).to eq(false)
      expect(result.errors.for(:subscription)[0].code).to eq("81905")
    end
  end

  describe "self.cancel!" do
    it "returns a updated subscription if valid" do
      subscription = Braintree::Subscription.create!(
        :payment_method_token => @credit_card.token,
        :price => 54.32,
        :plan_id => SpecHelper::TriallessPlan[:id],
      )

      updated_subscription = Braintree::Subscription.cancel!(subscription.id)
      expect(updated_subscription.status).to eq(Braintree::Subscription::Status::Canceled)
    end
  end

  describe "self.search" do
    describe "in_trial_period" do
      it "works in the affirmative" do
        id = rand(36**8).to_s(36)
        subscription_with_trial = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => "subscription1_#{id}",
        ).subscription

        subscription_without_trial = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :id => "subscription2_#{id}",
        ).subscription

        subscriptions_in_trial_period = Braintree::Subscription.search do |search|
          search.in_trial_period.is true
        end

        expect(subscriptions_in_trial_period).to include(subscription_with_trial)
        expect(subscriptions_in_trial_period).not_to include(subscription_without_trial)

        subscriptions_not_in_trial_period = Braintree::Subscription.search do |search|
          search.in_trial_period.is false
        end

        expect(subscriptions_not_in_trial_period).not_to include(subscription_with_trial)
        expect(subscriptions_not_in_trial_period).to include(subscription_without_trial)
      end
    end

    describe "search on merchant account id" do
      it "searches on merchant_account_id" do
        id = rand(36**8).to_s(36)
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => "subscription1_#{id}",
          :price => "11.38",
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.merchant_account_id.is subscription.merchant_account_id
          search.price.is "11.38"
        end

        # not testing for specific number since the
        # create subscriptions accumulate over time
        expect(collection.maximum_size).to be >= 1

        collection = Braintree::Subscription.search do |search|
          search.merchant_account_id.in subscription.merchant_account_id, "bogus_merchant_account_id"
          search.price.is "11.38"
        end

        expect(collection.maximum_size).to be >= 1

        collection = Braintree::Subscription.search do |search|
          search.merchant_account_id.is "bogus_merchant_account_id"
          search.price.is "11.38"
        end

        expect(collection.maximum_size).to eq(0)
      end
    end

    describe "id" do
      it "works using the is operator" do
        id = rand(36**8).to_s(36)
        subscription1 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => "subscription1_#{id}",
        ).subscription

        subscription2 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :id => "subscription2_#{id}",
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.id.is "subscription1_#{id}"
        end

        expect(collection).to include(subscription1)
        expect(collection).not_to include(subscription2)
      end
    end

    describe "merchant_account_id" do
      it "is searchable using the is or in operator" do
        subscription1 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :merchant_account_id => SpecHelper::DefaultMerchantAccountId,
          :price => "1",
        ).subscription

        subscription2 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
          :price => "1",
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.merchant_account_id.is SpecHelper::DefaultMerchantAccountId
          search.price.is "1"
        end

        expect(collection).to include(subscription1)
        expect(collection).not_to include(subscription2)

        collection = Braintree::Subscription.search do |search|
          search.merchant_account_id.in [SpecHelper::DefaultMerchantAccountId, SpecHelper::NonDefaultMerchantAccountId]
          search.price.is "1"
        end

        expect(collection).to include(subscription1)
        expect(collection).to include(subscription2)
      end
    end

    describe "plan_id" do
      it "works using the is operator" do
        trialless_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :price => "2",
        ).subscription

        trial_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :price => "2",
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.plan_id.is SpecHelper::TriallessPlan[:id]
          search.price.is "2"
        end

        expect(collection).to include(trialless_subscription)
        expect(collection).not_to include(trial_subscription)
      end
    end

    describe "price" do
      it "works using the is operator" do
        subscription_500 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :price => "5.00",
        ).subscription

        subscription_501 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :price => "5.01",
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.price.is "5.00"
        end

        expect(collection).to include(subscription_500)
        expect(collection).not_to include(subscription_501)
      end
    end

    describe "days_past_due" do
      it "is backwards-compatible for 'is'" do
        active_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :price => "6",
        ).subscription

        past_due_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :price => "6",
        ).subscription

        SpecHelper.make_past_due(past_due_subscription, 5)

        collection = Braintree::Subscription.search do |search|
          search.price.is "6.00"
          search.days_past_due.is 5
        end

        expect(collection).to include(past_due_subscription)
        expect(collection).not_to include(active_subscription)
        collection.each do |s|
          expect(s.status).to eq(Braintree::Subscription::Status::PastDue)
          expect(s.balance).to eq(BigDecimal("6.00"))
        end
      end

      it "passes a smoke test" do
        subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.days_past_due.between 1, 20
        end

        expect(collection).not_to include(subscription)
        collection.each do |s|
          expect(s.days_past_due).to be >= 1
          expect(s.days_past_due).to be <= 20
        end
      end
    end

    describe "billing_cycles_remaining" do
      it "passes a smoke test" do
        subscription_5 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
          :number_of_billing_cycles => 5,
        ).subscription

        subscription_9 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :number_of_billing_cycles => 10,
        ).subscription

        subscription_15 = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
          :number_of_billing_cycles => 15,
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.billing_cycles_remaining.between 5, 10
        end

        expect(collection).to include(subscription_5)
        expect(collection).to include(subscription_9)
        expect(collection).not_to include(subscription_15)
      end
    end

    describe "transaction_id" do
      it "returns matching results" do
        matching_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
        ).subscription

        non_matching_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
        ).subscription

        collection = Braintree::Subscription.search do |search|
          search.transaction_id.is matching_subscription.transactions.first.id
        end

        expect(collection).to include(matching_subscription)
        expect(collection).not_to include(non_matching_subscription)
      end
    end

    describe "next_billing_date" do
      it "returns matching results" do
        matching_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
        ).subscription

        non_matching_subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TrialPlan[:id],
        ).subscription

        five_days_from_now = Time.now + (5 * 24 * 60 * 60)
        collection = Braintree::Subscription.search do |search|
          search.next_billing_date >= five_days_from_now
        end

        expect(collection).to include(matching_subscription)
        expect(collection).not_to include(non_matching_subscription)
      end
    end

    context "created_at" do
      before(:each) do
        @subscription = Braintree::Subscription.create(
          :payment_method_token => @credit_card.token,
          :plan_id => SpecHelper::TriallessPlan[:id],
        ).subscription
        @created_at = @subscription.created_at
      end

      it "searches on created_at in UTC using between" do
        expect(@created_at).to be_utc

        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.between(
            @created_at - 60,
            @created_at + 60,
          )
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in UTC using geq" do
        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at >= @created_at - 1
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in UTC using leq" do
        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at <= @created_at + 1
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in UTC and finds nothing" do
        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.between(
            @created_at + 300,
            @created_at + 400,
          )
        end

        expect(collection.maximum_size).to eq(0)
      end

      it "searches on created_at in UTC using exact time" do
        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.is @created_at
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in local time using between" do
        now = Time.now

        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.between(
            now - 60,
            now + 60,
          )
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in local time using geq" do
        now = Time.now

        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at >= now - 60
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in local time using leq" do
        now = Time.now

        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at <= now + 60
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end

      it "searches on created_at in local time and finds nothing" do
        now = Time.now

        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.between(
            now + 300,
            now + 400,
          )
        end

        expect(collection.maximum_size).to eq(0)
      end

      it "searches on created_at with dates" do
        collection = Braintree::Subscription.search do |search|
          search.id.is @subscription.id
          search.created_at.between(
            Date.today - 1,
            Date.today + 1,
          )
        end

        expect(collection.maximum_size).to eq(1)
        expect(collection.first.id).to eq(@subscription.id)
      end
    end

    it "returns multiple results" do
      (110 - Braintree::Subscription.search.maximum_size).times do
        Braintree::Subscription.create(:payment_method_token => @credit_card.token, :plan_id => SpecHelper::TrialPlan[:id])
      end

      collection = Braintree::Subscription.search
      expect(collection.maximum_size).to be > 100

      subscriptions_ids = collection.map { |t| t.id }.uniq.compact
      expect(subscriptions_ids.size).to eq(collection.maximum_size)
    end
  end

  describe "self.retry_charge" do
    it "is successful with only subscription id" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
      SpecHelper.make_past_due(subscription)

      result = Braintree::Subscription.retry_charge(subscription.id)

      expect(result.success?).to eq(true)
      transaction = result.transaction

      expect(transaction.amount).to eq(subscription.price)
      expect(transaction.processor_authorization_code).not_to be_nil
      expect(transaction.type).to eq(Braintree::Transaction::Type::Sale)
      expect(transaction.status).to eq(Braintree::Transaction::Status::Authorized)
    end

    it "is successful with subscription id and amount" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
      SpecHelper.make_past_due(subscription)

      result = Braintree::Subscription.retry_charge(subscription.id, Braintree::Test::TransactionAmounts::Authorize)

      expect(result.success?).to eq(true)
      transaction = result.transaction

      expect(transaction.amount).to eq(BigDecimal(Braintree::Test::TransactionAmounts::Authorize))
      expect(transaction.processor_authorization_code).not_to be_nil
      expect(transaction.type).to eq(Braintree::Transaction::Type::Sale)
      expect(transaction.status).to eq(Braintree::Transaction::Status::Authorized)
    end

    it "is successful with subscription id and submit_for_settlement" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
      SpecHelper.make_past_due(subscription)

      result = Braintree::Subscription.retry_charge(subscription.id, Braintree::Test::TransactionAmounts::Authorize, true)

      expect(result.success?).to eq(true)
      transaction = result.transaction

      expect(transaction.amount).to eq(BigDecimal(Braintree::Test::TransactionAmounts::Authorize))
      expect(transaction.processor_authorization_code).not_to be_nil
      expect(transaction.type).to eq(Braintree::Transaction::Type::Sale)
      expect(transaction.status).to eq(Braintree::Transaction::Status::SubmittedForSettlement)
    end

    it "is successful with subscription id, amount and submit_for_settlement" do
      subscription = Braintree::Subscription.create(
        :payment_method_token => @credit_card.token,
        :plan_id => SpecHelper::TriallessPlan[:id],
      ).subscription
      SpecHelper.make_past_due(subscription)

      result = Braintree::Subscription.retry_charge(subscription.id, Braintree::Test::TransactionAmounts::Authorize, true)

      expect(result.success?).to eq(true)
      transaction = result.transaction

      expect(transaction.amount).to eq(BigDecimal(Braintree::Test::TransactionAmounts::Authorize))
      expect(transaction.processor_authorization_code).not_to be_nil
      expect(transaction.type).to eq(Braintree::Transaction::Type::Sale)
      expect(transaction.status).to eq(Braintree::Transaction::Status::SubmittedForSettlement)
    end
  end
end
