from __future__ import absolute_import
from mock import patch

from django.test import TransactionTestCase
from django.test.client import RequestFactory

import stripe
from stripe.resource import StripeObject

from corehq.apps.accounting.models import PaymentRecord, PaymentMethod, BillingAccount
from corehq.apps.accounting.payment_handlers import CreditStripePaymentHandler
from corehq.apps.accounting.tests import generator
from corehq.apps.domain.models import Domain


class TestCreditStripePaymentHandler(TransactionTestCase):

    def setUp(self):
        super(TestCreditStripePaymentHandler, self).setUp()
        generator.instantiate_accounting()
        self.domain = Domain(name='test-domain')
        self.domain.save()
        self.payment_method = PaymentMethod()
        self.payment_method.save()
        self.account, _ = BillingAccount.get_or_create_account_by_domain(
            self.domain.name, created_by='webuser@test.com'
        )

    def tearDown(self):
        self.domain.delete()
        PaymentRecord.objects.all().delete()
        self.payment_method.delete()
        self.account.delete()
        super(TestCreditStripePaymentHandler, self).tearDown()

    @patch.object(stripe.Charge, 'create')
    def test_working_process_request(self, mock_create):
        transaction_id = 'stripe_charge_id'
        mock_create.return_value = StripeObject(id=transaction_id)

        self._call_process_request()

        self.assertEqual(PaymentRecord.objects.count(), 1)
        self.assertEqual(PaymentRecord.objects.all()[0].transaction_id, transaction_id)
        self.assertEqual(mock_create.call_count, 1)

    @patch.object(stripe.Charge, 'create')
    def test_when_stripe_errors_no_payment_record_exists(self, mock_create):
        mock_create.side_effect = Exception

        self._call_process_request()

        self.assertEqual(PaymentRecord.objects.count(), 0)

    @patch.object(stripe.Charge, 'create')
    @patch.object(PaymentRecord, 'create_record')
    def test_when_create_record_fails_stripe_is_not_charged(self, mock_create_record, mock_create):
        mock_create_record.side_effect = Exception

        try:
            self._call_process_request()
        except:
            pass

        self.assertEqual(PaymentRecord.objects.count(), 0)
        self.assertEqual(mock_create.call_count, 0)

    def _call_process_request(self):
        CreditStripePaymentHandler(
            self.payment_method, self.domain, self.account, post_data={}
        ).process_request(
            RequestFactory().post('', {'amount': 1})
        )

@@ -467,16 +467,114 @@
 omain%0A%0A%0A
+class MockFailingStripeObject(object):%0A%0A    @property%0A    def id(self):%0A        raise Exception%0A%0A%0A
 class Te
@@ -1730,16 +1730,409 @@
 nt, 1)%0A%0A
+    @patch.object(stripe.Charge, 'create')%0A    def test_failure_after_checkpoint(self, mock_create):%0A        mock_create.return_value = MockFailingStripeObject()%0A%0A        self._call_process_request()%0A%0A        self.assertEqual(PaymentRecord.objects.count(), 1)%0A        self.assertEqual(PaymentRecord.objects.all()%5B0%5D.transaction_id, 'temp')%0A        self.assertEqual(mock_create.call_count, 1)%0A%0A
     @pat

test case when there is an exception thrown after the atomic block