def test_atomicity_when_order_save_errors(self, requests_mock):
        """
        Test that if the order.mark_as_paid() call fails, non of the changes persists.
        """
        session = PaymentGatewaySessionFactory()
        original_session_status = session.status
        url = govuk_url(f'payments/{session.govuk_payment_id}')
        requests_mock.get(
            url,
            status_code=200,
            json={
                'state': {
                    'status': 'success'
                },
            },
        )
        session.order.mark_as_paid = mock.MagicMock(side_effect=Exception())

        with pytest.raises(Exception):
            session.refresh_from_govuk_payment()

        session.refresh_from_db()
        assert session.status == original_session_status

        assert requests_mock.call_count == 1
    def test_with_different_govuk_payment_status_updates_session(
            self, status, requests_mock):
        """
        Test that if the GOV.UK payment status is not the same as the payment gateway session one,
        the record is updated.
        """
        # choose an initial status != from the govuk one to test the update
        initial_status = PaymentGatewaySessionStatus.created
        if initial_status == status:
            initial_status = PaymentGatewaySessionStatus.started

        session = PaymentGatewaySessionFactory(status=initial_status)
        url = govuk_url(f'payments/{session.govuk_payment_id}')
        requests_mock.get(
            url,
            status_code=200,
            json={
                'state': {
                    'status': status
                },
            },
        )

        assert session.refresh_from_govuk_payment()

        session.refresh_from_db()
        assert session.status == status

        assert requests_mock.call_count == 1
    def test_atomicity_when_govuk_pay_errors(self, requests_mock):
        """
        Test that if GOV.UK Pay errors, none of the changes persists.
        """
        session = PaymentGatewaySessionFactory()
        original_session_status = session.status

        url = govuk_url(f'payments/{session.govuk_payment_id}')
        requests_mock.get(url, status_code=500)

        with pytest.raises(GOVUKPayAPIException):
            assert session.refresh_from_govuk_payment()

        session.refresh_from_db()
        assert session.status == original_session_status

        assert requests_mock.call_count == 1
    def test_with_govuk_payment_success_updates_order(self, requests_mock):
        """
        Test that if the GOV.UK payment status is `success` and the payment gateway session is
        out of date, the record is updated, the related order marked as `paid` and an OMIS
        `payment.Payment` record created from the GOV.UK response data one.
        """
        order = OrderWithAcceptedQuoteFactory()
        session = PaymentGatewaySessionFactory(
            status=PaymentGatewaySessionStatus.created,
            order=order,
        )
        url = govuk_url(f'payments/{session.govuk_payment_id}')
        response_json = {
            'amount': order.total_cost,
            'state': {
                'status': 'success'
            },
            'email': '*****@*****.**',
            'created_date': '2018-02-13T14:56:56.734Z',
            'reference': '12345',
            'card_details': {
                'last_digits_card_number': '1111',
                'cardholder_name': 'John Doe',
                'expiry_date': '01/20',
                'billing_address': {
                    'line1': 'line 1 address',
                    'line2': 'line 2 address',
                    'postcode': 'SW1A 1AA',
                    'city': 'London',
                    'country': 'GB',
                },
                'card_brand': 'Visa',
            },
        }
        requests_mock.get(url, status_code=200, json=response_json)

        assert session.refresh_from_govuk_payment()

        # check session
        session.refresh_from_db()
        assert session.status == PaymentGatewaySessionStatus.success

        # check order
        order.refresh_from_db()
        assert order.status == OrderStatus.paid

        # check payment object
        assert Payment.objects.filter(order=order).count() == 1

        payment = Payment.objects.filter(order=order).first()
        assert payment.amount == response_json['amount']
        assert payment.method == PaymentMethod.card
        assert payment.received_on == dateutil_parse('2018-02-13').date()
        assert payment.transaction_reference == '12345'

        assert payment.cardholder_name == 'John Doe'
        assert payment.card_brand == 'Visa'
        assert payment.billing_email == '*****@*****.**'
        assert payment.billing_address_1 == 'line 1 address'
        assert payment.billing_address_2 == 'line 2 address'
        assert payment.billing_address_town == 'London'
        assert payment.billing_address_postcode == 'SW1A 1AA'
        assert payment.billing_address_country == 'GB'

        assert requests_mock.call_count == 1