mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			134 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import mock
 | 
						|
import os
 | 
						|
from typing import Any
 | 
						|
import ujson
 | 
						|
 | 
						|
import stripe
 | 
						|
from stripe.api_resources.list_object import ListObject
 | 
						|
 | 
						|
from zerver.lib.test_classes import ZulipTestCase
 | 
						|
from zerver.models import Realm, UserProfile
 | 
						|
from zilencer.lib.stripe import StripeError, save_stripe_token, catch_stripe_errors
 | 
						|
from zilencer.models import Customer
 | 
						|
 | 
						|
fixture_data_file = open(os.path.join(os.path.dirname(__file__), 'fixtures/stripe.json'), 'r')
 | 
						|
fixture_data = ujson.load(fixture_data_file)
 | 
						|
 | 
						|
def mock_list_sources(*args: Any, **kwargs: Any) -> ListObject:
 | 
						|
    return stripe.util.convert_to_stripe_object(fixture_data["list_sources"])
 | 
						|
 | 
						|
def mock_create_source(*args: Any, **kwargs: Any) -> ListObject:
 | 
						|
    return stripe.util.convert_to_stripe_object(fixture_data["create_source"])
 | 
						|
 | 
						|
def mock_create_customer(*args: Any, **kwargs: Any) -> ListObject:
 | 
						|
    return stripe.util.convert_to_stripe_object(fixture_data["create_customer"])
 | 
						|
 | 
						|
def mock_retrieve_customer(*args: Any, **kwargs: Any) -> ListObject:
 | 
						|
    return stripe.util.convert_to_stripe_object(fixture_data["retrieve_customer"])
 | 
						|
 | 
						|
class StripeTest(ZulipTestCase):
 | 
						|
    def setUp(self) -> None:
 | 
						|
        self.token = "token"
 | 
						|
        self.user = self.example_user("iago")
 | 
						|
        self.realm = self.user.realm
 | 
						|
 | 
						|
    @mock.patch("zilencer.lib.stripe.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key")
 | 
						|
    @mock.patch("zilencer.lib.stripe.billing_logger.info")
 | 
						|
    @mock.patch("stripe.api_resources.list_object.ListObject.create", side_effect=mock_create_source)
 | 
						|
    @mock.patch("stripe.api_resources.list_object.ListObject.list", side_effect=mock_list_sources)
 | 
						|
    @mock.patch("stripe.Customer.create", side_effect=mock_create_customer)
 | 
						|
    @mock.patch("stripe.Customer.retrieve", side_effect=mock_retrieve_customer)
 | 
						|
    @mock.patch("stripe.api_resources.card.Card.save")
 | 
						|
    @mock.patch("stripe.api_resources.customer.Customer.save")
 | 
						|
    def test_save_stripe_token(self, mock_save_customer: mock.Mock, mock_save_card: mock.Mock,
 | 
						|
                               mock_retrieve_customer: mock.Mock, mock_create_customer: mock.Mock,
 | 
						|
                               mock_list_sources: mock.Mock, mock_create_source: mock.Mock,
 | 
						|
                               mock_billing_logger_info: mock.Mock) -> None:
 | 
						|
        self.assertFalse(Customer.objects.filter(realm=self.realm))
 | 
						|
        number_of_cards = save_stripe_token(self.user, self.token)
 | 
						|
        self.assertEqual(number_of_cards, 1)
 | 
						|
        description = "{} ({})".format(self.realm.name, self.realm.string_id)
 | 
						|
        mock_create_customer.assert_called_once_with(description=description, source=self.token,
 | 
						|
                                                     metadata={'string_id': self.realm.string_id})
 | 
						|
        mock_list_sources.assert_called_once()
 | 
						|
        mock_save_card.assert_called_once()
 | 
						|
        mock_billing_logger_info.assert_called()
 | 
						|
        customer_object = Customer.objects.get(realm=self.realm)
 | 
						|
 | 
						|
        # Add another card
 | 
						|
        number_of_cards = save_stripe_token(self.user, self.token)
 | 
						|
        # Note: customer.sources.list is mocked to return 2 cards all the time.
 | 
						|
        self.assertEqual(number_of_cards, 2)
 | 
						|
        mock_retrieve_customer.assert_called_once_with(customer_object.stripe_customer_id)
 | 
						|
        create_source_metadata = {'added_user_id': self.user.id, 'added_user_email': self.user.email}
 | 
						|
        mock_create_source.assert_called_once_with(metadata=create_source_metadata, source='token')
 | 
						|
        mock_save_customer.assert_called_once()
 | 
						|
        mock_billing_logger_info.assert_called()
 | 
						|
 | 
						|
    @mock.patch("zilencer.lib.stripe.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key")
 | 
						|
    @mock.patch("zilencer.lib.stripe.billing_logger.error")
 | 
						|
    def test_errors(self, mock_billing_logger_error: mock.Mock) -> None:
 | 
						|
        @catch_stripe_errors
 | 
						|
        def raise_invalid_request_error() -> None:
 | 
						|
            raise stripe.error.InvalidRequestError("Request req_oJU621i6H6X4Ez: No such token: x",
 | 
						|
                                                   None)
 | 
						|
        with self.assertRaisesRegex(StripeError, "Something went wrong. Please try again or "):
 | 
						|
            raise_invalid_request_error()
 | 
						|
        mock_billing_logger_error.assert_called()
 | 
						|
 | 
						|
        @catch_stripe_errors
 | 
						|
        def raise_card_error() -> None:
 | 
						|
            error_message = "The card number is not a valid credit card number."
 | 
						|
            json_body = {"error": {"message": error_message}}
 | 
						|
            raise stripe.error.CardError(error_message, "number", "invalid_number",
 | 
						|
                                         json_body=json_body)
 | 
						|
        with self.assertRaisesRegex(StripeError,
 | 
						|
                                    "The card number is not a valid credit card number."):
 | 
						|
            raise_card_error()
 | 
						|
        mock_billing_logger_error.assert_called()
 | 
						|
 | 
						|
        @catch_stripe_errors
 | 
						|
        def raise_exception() -> None:
 | 
						|
            raise Exception
 | 
						|
        with self.assertRaises(Exception):
 | 
						|
            raise_exception()
 | 
						|
        mock_billing_logger_error.assert_called()
 | 
						|
 | 
						|
    @mock.patch("zilencer.views.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key")
 | 
						|
    @mock.patch("zilencer.lib.stripe.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key")
 | 
						|
    def test_billing_page_view_permissions(self) -> None:
 | 
						|
        result = self.client_get("/billing/")
 | 
						|
        self.assertEqual(result.status_code, 302)
 | 
						|
        self.assertEqual(result["Location"], "/login?next=/billing/")
 | 
						|
 | 
						|
        self.login(self.example_email("hamlet"))
 | 
						|
        result = self.client_get("/billing/")
 | 
						|
        message = ("You should be an administrator of the organization {} to view this page."
 | 
						|
                   .format(self.realm.name))
 | 
						|
        self.assert_in_success_response([message], result)
 | 
						|
        self.assert_not_in_success_response(["stripe_publishable_key"], result)
 | 
						|
 | 
						|
        self.login(self.example_email("iago"))
 | 
						|
        result = self.client_get("/billing/")
 | 
						|
        self.assert_not_in_success_response([message], result)
 | 
						|
        self.assert_in_success_response(["stripe_publishable_key"], result)
 | 
						|
 | 
						|
    def test_billing_page_view_add_card(self) -> None:
 | 
						|
        self.login(self.example_email("iago"))
 | 
						|
 | 
						|
        with mock.patch("zilencer.views.save_stripe_token", side_effect=StripeError("Stripe error")):
 | 
						|
            result = self.client_post("/billing/", {"stripeToken": self.token})
 | 
						|
            self.assert_in_success_response(["Stripe error"], result)
 | 
						|
            self.assert_not_in_success_response(["The card has been saved successfully"], result)
 | 
						|
 | 
						|
        with mock.patch("zilencer.views.save_stripe_token", return_value=1), \
 | 
						|
                mock.patch("zilencer.views.count_stripe_cards", return_value=1):
 | 
						|
            result = self.client_post("/billing/", {"stripeToken": self.token})
 | 
						|
            self.assert_in_success_response(["The card has been saved successfully"], result)
 | 
						|
 | 
						|
        # Add another card
 | 
						|
        with mock.patch("zilencer.views.save_stripe_token", return_value=2), \
 | 
						|
                mock.patch("zilencer.views.count_stripe_cards", return_value=2):
 | 
						|
            result = self.client_post("/billing/", {"stripeToken": self.token})
 | 
						|
            self.assert_in_success_response(["The card has been saved successfully"], result)
 |