- Updated all test files to remove calls to Stripe
- Updated all test files to use monkeypatch, mock or mockito
This commit is contained in:
Jackie Munroe 2019-07-26 09:42:53 -07:00 коммит произвёл Scott Idler
Родитель 65cd703045
Коммит 0d29f8c1cf
6 изменённых файлов: 457 добавлений и 510 удалений

Просмотреть файл

@ -31,7 +31,6 @@ before_install:
stages:
- name: Graph
- name: Unit Test
- name: Docker Build
jobs:
include:
@ -41,7 +40,4 @@ jobs:
- doit draw
- stage: Unit Test
script:
- doit test
- stage: Docker Build
script:
- docker-compose build
- doit test

Просмотреть файл

@ -101,6 +101,7 @@ def _get_all_plans():
def retrieve_stripe_subscriptions(customer: Customer) -> list:
logger.info("customer", customer=customer)
try:
customer_subscriptions_data = customer.subscriptions
customer_subscriptions = customer_subscriptions_data.get("data")
@ -129,7 +130,9 @@ def cancel_subscription(uid, sub_id) -> FlaskResponse:
]:
Subscription.modify(sub_id, cancel_at_period_end=True)
updated_customer = fetch_customer(g.subhub_account, uid)
logger.info("updated customer", updated_customer=updated_customer)
subs = retrieve_stripe_subscriptions(updated_customer)
logger.info("subs", subs=subs, type=type(subs))
for sub in subs:
if sub["cancel_at_period_end"] and sub["id"] == sub_id:
return {"message": "Subscription cancellation successful"}, 201

Просмотреть файл

@ -6,7 +6,7 @@ from subhub.exceptions import SubHubError, IntermittentError, ClientError, Serve
from subhub.exceptions import SecretStringMissingError
def test_SubHubError():
def test_subhub_error():
message = "message"
status_code = 513
payload = dict(some="payload")
@ -17,7 +17,7 @@ def test_SubHubError():
)
def test_IntermittentError():
def test_intermittent_error():
message = "message"
ex1 = IntermittentError(message)
assert ex1.args[0] == message
@ -34,7 +34,7 @@ def test_IntermittentError():
assert ex2.to_dict() == dict(message=message, some="payload")
def test_ClientError():
def test_client_error():
message = "message"
ex1 = ClientError(message)
assert ex1.args[0] == message
@ -51,7 +51,7 @@ def test_ClientError():
assert ex2.to_dict() == dict(message=message, some="payload")
def test_ServerError():
def test_server_error():
message = "message"
ex1 = ServerError(message)
assert ex1.args[0] == message

Просмотреть файл

@ -4,13 +4,19 @@
import os
import uuid
import json
import asyncio
import pytest
import stripe
from flask import g
from stripe.error import InvalidRequestError
from unittest.mock import Mock, MagicMock, PropertyMock
from pynamodb.exceptions import DeleteError
from stripe import Customer
from subhub.exceptions import ClientError
from subhub.app import create_app
from unittest.mock import Mock, MagicMock, PropertyMock, patch
from mockito import when, mock, unstub, ANY
from subhub.sub import payments
from subhub.customer import (
@ -21,12 +27,34 @@ from subhub.customer import (
from subhub.tests.unit.stripe.utils import MockSubhubUser
from subhub.log import get_logger
from subhub.cfg import CFG
from pynamodb.exceptions import DeleteError
logger = get_logger()
def test_create_customer_invalid_origin_system():
class MockCustomer:
id = 123
object = "customer"
subscriptions = [{"data": "somedata"}]
metadata = {"userid": "123"}
def properties(self, cls):
return [i for i in cls.__dict__.keys() if i[:1] != "_"]
def get(self, key, default=None):
properties = self.properties(MockCustomer)
logger.info("mock properties", properties=properties)
if key in properties:
return key
else:
return default
def __iter__(self):
yield "subscriptions", self.subscriptions
def test_create_customer_invalid_origin_system(monkeypatch):
"""
GIVEN create a stripe customer
WHEN An invalid origin system is provided
@ -66,50 +94,85 @@ def test_existing_or_new_customer_invalid_origin_system():
assert msg == str(request_error.value)
def test_create_customer_tok_visa():
def test_create_customer(monkeypatch):
"""
GIVEN create a stripe customer
WHEN provided a test visa token and test fxa
THEN validate the customer metadata is correct
"""
mock_possible_customers = MagicMock()
data = PropertyMock(return_value=[])
type(mock_possible_customers).data = data
mock_customer = MagicMock()
id = PropertyMock(return_value="cust_123")
type(mock_customer).id = id
subhub_account = MagicMock()
mock_user = MagicMock()
user_id = PropertyMock(return_value="user_123")
cust_id = PropertyMock(return_value="cust_123")
type(mock_user).user_id = user_id
type(mock_user).cust_id = cust_id
mock_save = MagicMock(return_value=True)
subhub_account.new_user = mock_user
subhub_account.save_user = mock_save
monkeypatch.setattr("stripe.Customer.list", mock_possible_customers)
monkeypatch.setattr("stripe.Customer.create", mock_customer)
customer = create_customer(
g.subhub_account,
user_id="test_mozilla",
subhub_account,
user_id="user_123",
source_token="tok_visa",
email="test_visa@tester.com",
origin_system="Test_system",
display_name="John Tester",
)
pytest.customer = customer
assert customer["metadata"]["userid"] == "test_mozilla"
assert customer is not None
def test_create_customer_tok_mastercard():
"""
GIVEN create a stripe customer
WHEN provided a test mastercard token and test userid
THEN validate the customer metadata is correct
"""
customer = create_customer(
g.subhub_account,
user_id="test_mozilla",
source_token="tok_mastercard",
email="test_mastercard@tester.com",
origin_system="Test_system",
display_name="John Tester",
)
assert customer["metadata"]["userid"] == "test_mozilla"
def test_create_customer_tok_invalid():
def test_create_customer_tok_invalid(monkeypatch):
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
mock_possible_customers = MagicMock()
data = PropertyMock(return_value=[])
type(mock_possible_customers).data = data
mock_customer_error = Mock(
side_effect=InvalidRequestError(
message="Customer instance has invalid ID",
param="customer_id",
code="invalid",
)
)
subhub_account = MagicMock()
mock_user = MagicMock()
user_id = PropertyMock(return_value="user_123")
cust_id = PropertyMock(return_value="cust_123")
type(mock_user).user_id = user_id
type(mock_user).cust_id = cust_id
mock_save = MagicMock(return_value=True)
subhub_account.new_user = mock_user
subhub_account.save_user = mock_save
monkeypatch.setattr("stripe.Customer.list", mock_possible_customers)
monkeypatch.setattr("stripe.Customer.create", mock_customer_error)
with pytest.raises(InvalidRequestError):
customer = create_customer(
g.subhub_account,
create_customer(
subhub_account,
user_id="test_mozilla",
source_token="tok_invalid",
email="test_invalid@tester.com",
@ -118,388 +181,222 @@ def test_create_customer_tok_invalid():
)
def test_create_customer_tok_avsFail():
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
customer = create_customer(
g.subhub_account,
user_id="test_mozilla",
source_token="tok_avsFail",
email="test_avsfail@tester.com",
origin_system="Test_system",
display_name="John Tester",
)
assert customer["metadata"]["userid"] == "test_mozilla"
def test_create_customer_tok_avsUnchecked():
"""
GIVEN create a stripe customer
WHEN provided an invalid test token and test userid
THEN validate the customer metadata is correct
"""
customer = create_customer(
g.subhub_account,
user_id="test_mozilla",
source_token="tok_avsUnchecked",
email="test_avsunchecked@tester.com",
origin_system="Test_system",
display_name="John Tester",
)
assert customer["metadata"]["userid"] == "test_mozilla"
def test_subscribe_customer(create_customer_for_processing):
def test_subscribe_customer(monkeypatch):
"""
GIVEN create a subscription
WHEN provided a customer and plan
THEN validate subscription is created
"""
customer = create_customer_for_processing
subscription = subscribe_customer(customer, "plan_EtMcOlFMNWW4nd")
assert subscription["plan"]["active"]
mock_customer = MagicMock()
id = PropertyMock(return_value="cust_123")
type(mock_customer).id = id
mock_subscription = MagicMock()
monkeypatch.setattr("stripe.Subscription.create", mock_subscription)
subscription = subscribe_customer(mock_customer, "plan_EtMcOlFMNWW4nd")
assert subscription is not None
def test_subscribe_customer_invalid_plan(create_customer_for_processing):
def test_subscribe_customer_invalid_data(monkeypatch):
"""
GIVEN create a subscription
WHEN provided a customer and plan
THEN validate subscription is created
"""
customer = create_customer_for_processing
try:
subscribe_customer(customer, "plan_notvalid")
except Exception as e:
exception = e
assert isinstance(exception, InvalidRequestError)
assert "Unable to create plan" in exception.user_message
mock_customer = MagicMock()
id = PropertyMock(return_value="cust_123")
type(mock_customer).id = id
mock_subscribe = Mock(side_effect=InvalidRequestError)
monkeypatch.setattr("stripe.Subscription.create", mock_subscribe)
with pytest.raises(InvalidRequestError):
subscribe_customer(mock_customer, "invalid_plan_id")
def test_create_subscription_with_valid_data():
"""
GIVEN create a subscription
WHEN provided a api_token, userid, pmt_token, plan_id, cust_id
THEN validate subscription is created
"""
uid = uuid.uuid4()
subscription, code = payments.subscribe_to_plan(
"validcustomer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": "valid@{}customer.com".format(uid),
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
assert 201 == code
payments.cancel_subscription(
"validcustomer", subscription["subscriptions"][0]["subscription_id"]
)
g.subhub_account.remove_from_db("validcustomer")
def test_subscribe_customer_existing(create_customer_for_processing):
def test_subscribe_customer_existing(app, monkeypatch):
"""
GIVEN create a subscription
WHEN provided a customer and plan
THEN validate subscription is created
"""
uid = uuid.uuid4()
subscription, code = payments.subscribe_to_plan(
"validcustomer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
subscription2, code2 = payments.subscribe_to_plan(
"validcustomer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
assert 409 == code2
payments.cancel_subscription(
"validcustomer", subscription["subscriptions"][0]["subscription_id"]
)
g.subhub_account.remove_from_db("validcustomer")
client = app.app.test_client()
plans_data = [
{
"id": "plan_123",
"product": "prod_1",
"interval": "month",
"amount": 25,
"currency": "usd",
"nickname": "Plan 1",
},
{
"id": "plan_2",
"product": "prod_1",
"interval": "year",
"amount": 250,
"currency": "usd",
"nickname": "Plan 2",
},
]
def test_create_subscription_with_invalid_payment_token():
"""
GIVEN a api_token, userid, invalid pmt_token, plan_id, email
WHEN the pmt_token is invalid
THEN a StripeError should be raised
"""
exception = None
try:
subscription, code = payments.subscribe_to_plan(
"invalid_test",
{
"pmt_token": "tok_invalid",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": "invalid_test@test.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
product_data = {"name": "Product 1"}
plans = Mock(return_value=plans_data)
product = Mock(return_value=product_data)
subhub_account = MagicMock()
get_user = MagicMock()
user_id = PropertyMock(return_value="user123")
cust_id = PropertyMock(return_value="cust123")
type(get_user).user_id = user_id
type(get_user).cust_id = cust_id
subhub_account.get_user = get_user
stripe_customer = Mock(
return_value={
"metadata": {"userid": "user123"},
"subscriptions": {
"data": [{"plan": {"id": "plan_123"}, "status": "active"}]
},
)
except Exception as e:
exception = e
g.subhub_account.remove_from_db("invalid_test")
assert isinstance(exception, InvalidRequestError)
assert "Unable to create customer." in exception.user_message
def test_create_subscription_with_invalid_plan_id(app):
"""
GIVEN a api_token, userid, pmt_token, plan_id, email
WHEN the plan_id provided is invalid
THEN a StripeError is raised
"""
exception = None
try:
plan, code = payments.subscribe_to_plan(
"invalid_plan",
{
"pmt_token": "tok_visa",
"plan_id": "plan_abc123",
"email": "invalid_plan@tester.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
"sources": {
"data": [
{
"funding": "blah",
"last4": "1234",
"exp_month": "02",
"exp_year": "2020",
}
]
},
)
except Exception as e:
exception = e
g.subhub_account.remove_from_db("invalid_plan")
assert isinstance(exception, InvalidRequestError)
assert "No such plan:" in exception.user_message
def test_list_all_plans_valid():
"""
GIVEN should list all available plans
WHEN provided an api_token,
THEN validate able to list all available plans
"""
plans, code = payments.list_all_plans()
assert len(plans) > 0
assert 200 == code
def test_cancel_subscription_with_valid_data(app, create_subscription_for_processing):
"""
GIVEN should cancel an active subscription
WHEN provided a api_token, and subscription id
THEN validate should cancel subscription
"""
subscription, code = create_subscription_for_processing
cancelled, code = payments.cancel_subscription(
"process_test", subscription["subscriptions"][0]["subscription_id"]
}
)
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
g.subhub_account.remove_from_db("process_test")
mock_true = Mock(return_value=True)
monkeypatch.setattr("stripe.Plan.list", plans)
monkeypatch.setattr("stripe.Product.retrieve", product)
monkeypatch.setattr("subhub.sub.payments.has_existing_plan", mock_true)
monkeypatch.setattr("flask.g.subhub_account", subhub_account)
monkeypatch.setattr("stripe.Customer.retrieve", stripe_customer)
path = "v1/customer/user123/subscriptions"
data = {
"pmt_token": "tok_visa",
"plan_id": "plan_123",
"origin_system": "Test_system",
"email": "user123@example.com",
"display_name": "John Tester",
}
response = client.post(
path,
headers={"Authorization": "fake_payment_api_key"},
data=json.dumps(data),
content_type="application/json",
)
logger.info("response data", data=response.data)
assert response.status_code == 409
def test_delete_customer(app, create_subscription_for_processing):
def test_cancel_subscription_no_subscription_found(monkeypatch):
"""
GIVEN should cancel an active subscription,
delete customer from payment provider and database
WHEN provided a user id
THEN validate user is deleted from payment provider and database
GIVEN call to cancel subscription
WHEN there is no active subscription
THEN return the appropriate message
Subscription.modify(sub_id, cancel_at_period_end=True)
"""
message, code = payments.delete_customer("process_test")
assert message["message"] == "Customer deleted successfully"
deleted_message, code = payments.subscription_status("process_test")
assert "Customer does not exist" in deleted_message["message"]
with monkeypatch.context() as m:
user = Mock(return_value=MockSubhubUser())
subscription_data = {
"id": "sub_123",
"status": "deleted",
"current_period_end": 1566833524,
"current_period_start": 1564155124,
"ended_at": None,
"plan": {"id": "plan_123", "nickname": "Monthly VPN Subscription"},
"cancel_at_period_end": False,
}
customer = MagicMock(
return_value={
"id": "123",
"cust_id": "cust_123",
"metadata": {"userid": "123"},
"subscriptions": {"data": [subscription_data]},
}
)
cancel_response = mock(
{
"id": "cus_tester3",
"object": "customer",
"subscriptions": {"data": []},
"sources": {"data": [{"id": "sub_123", "cancel_at_period_end": True}]},
},
spec=Customer,
)
delete_response = mock(
{"id": "cus_tester3", "object": "customer", "sources": []}, spec=Customer
)
when(Customer).delete_source("cus_tester3", "src_123").thenReturn(
delete_response
)
m.setattr("flask.g.subhub_account.get_user", user)
m.setattr("stripe.Customer.retrieve", customer)
m.setattr("subhub.customer.fetch_customer", customer)
m.setattr("subhub.sub.payments.retrieve_stripe_subscriptions", cancel_response)
cancel_sub, code = payments.cancel_subscription("123", "sub_123")
logger.info("cancel sub", cancel_sub=cancel_sub)
assert "Subscription not available." in cancel_sub["message"]
assert code == 400
def test_delete_customer_bad_user(app, create_subscription_for_processing):
def test_cancel_subscription_without_valid_user(monkeypatch):
"""
GIVEN should cancel an active subscription,
delete customer from payment provider and database
WHEN provided a user id
THEN validate user is deleted from payment provider and database
GIVEN call to cancel subscription
WHEN an invalid customer is sent
THEN return the appropriate message
"""
message, code = payments.delete_customer("process_test2")
assert message["message"] == "Customer does not exist."
customer = Mock(return_value=None)
monkeypatch.setattr("subhub.customer.fetch_customer", customer)
cancel_sub, code = payments.cancel_subscription("bob_123", "sub_123")
assert "Customer does not exist." in cancel_sub["message"]
assert code == 404
def test_delete_user_from_db(app, create_subscription_for_processing):
"""
GIVEN should delete user from user table
WHEN provided with a valid user id
THEN add to deleted users table
"""
deleted_user = payments.delete_user("process_test", "sub_id", "origin")
logger.info("deleted user from db", deleted_user=deleted_user)
assert deleted_user is True
def test_delete_user_from_db2(app, create_subscription_for_processing, monkeypatch):
"""
GIVEN raise DeleteError
WHEN an entry cannot be removed from the database
THEN validate error message
"""
delete_error = Mock(side_effect=DeleteError())
monkeypatch.setattr("subhub.db.SubHubAccount.remove_from_db", delete_error)
with pytest.raises(DeleteError) as request_error:
payments.delete_user("process_test_2", "sub_id", "origin")
msg = "Error deleting item"
assert msg in str(request_error.value)
def test_add_user_to_deleted_users_record(app, create_customer_for_processing):
def test_add_user_to_deleted_users_record(monkeypatch):
"""
GIVEN Add user to deleted users record
WHEN provided a user id, cust id and origin system
THEN return subhud_deleted user
"""
customer = Mock(
return_value={
"user_id": "process_customer",
"cust_id": "cus_123",
"origin_system": "Test_system",
}
)
monkeypatch.setattr("flask.g.subhub_account.get_user", customer)
to_delete = g.subhub_account.get_user("process_customer")
logger.info("delete", deleted_user=to_delete)
deleted_user = payments.add_user_to_deleted_users_record(
user_id=to_delete.user_id,
cust_id=to_delete.cust_id,
origin_system=to_delete.origin_system,
user_id=to_delete["user_id"],
cust_id=to_delete["cust_id"],
origin_system=to_delete["origin_system"],
)
assert deleted_user.user_id == "process_customer"
assert deleted_user.origin_system == "Test_system"
assert "cus_" in deleted_user.cust_id
def test_cancel_subscription_with_valid_data_multiple_subscriptions_remove_first():
"""
GIVEN a user with multiple subscriptions
WHEN the first subscription is cancelled
THEN the specified subscription is cancelled
"""
uid = uuid.uuid4()
subscription1, code1 = payments.subscribe_to_plan(
"valid_customer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
subscription2, code2 = payments.subscribe_to_plan(
"valid_customer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_F4G9jB3x5i6Dpj",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
# cancel the first subscription created
cancelled, code = payments.cancel_subscription(
"valid_customer", subscription1["subscriptions"][0]["subscription_id"]
)
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
# clean up test data created
cancelled, code = payments.cancel_subscription(
"valid_customer", subscription2["subscriptions"][0]["subscription_id"]
)
g.subhub_account.remove_from_db("valid_customer")
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
def test_cancel_subscription_with_valid_data_multiple_subscriptions_remove_second():
"""
GIVEN a user with multiple subscriptions
WHEN the second subscription is cancelled
THEN the specified subscription is cancelled
"""
uid = uuid.uuid4()
subscription1, code1 = payments.subscribe_to_plan(
"valid_customer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_EtMcOlFMNWW4nd",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
subscription2, code2 = payments.subscribe_to_plan(
"valid_customer",
{
"pmt_token": "tok_visa",
"plan_id": "plan_F4G9jB3x5i6Dpj",
"email": f"valid@{uid}customer.com",
"origin_system": "Test_system",
"display_name": "Jon Tester",
},
)
# cancel the second subscription created
cancelled, code = payments.cancel_subscription(
"valid_customer", subscription2["subscriptions"][0]["subscription_id"]
)
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
# clean up test data created
cancelled, code = payments.cancel_subscription(
"valid_customer", subscription1["subscriptions"][0]["subscription_id"]
)
g.subhub_account.remove_from_db("valid_customer")
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
def test_cancel_subscription_with_invalid_data(app, create_subscription_for_processing):
subscription, code = create_subscription_for_processing
if subscription.get("subscriptions"):
cancelled, code = payments.cancel_subscription(
"process_test",
subscription["subscriptions"][0]["subscription_id"] + "invalid",
)
assert cancelled["message"] == "Subscription not available."
assert 400 == code
g.subhub_account.remove_from_db("process_test")
def test_cancel_subscription_already_cancelled(app, create_subscription_for_processing):
subscription, code = create_subscription_for_processing
cancelled, code = payments.cancel_subscription(
"process_test", subscription["subscriptions"][0]["subscription_id"]
)
cancelled2, code2 = payments.cancel_subscription(
"process_test", subscription["subscriptions"][0]["subscription_id"]
)
assert cancelled["message"] == "Subscription cancellation successful"
assert 201 == code
assert cancelled2["message"] == "Subscription cancellation successful"
assert 201 == code2
g.subhub_account.remove_from_db("process_test")
def test_cancel_subscription_with_invalid_subhub_user(app):
def test_cancel_subscription_with_invalid_subhub_user(monkeypatch):
"""
GIVEN an active subscription
WHEN provided an api_token and an invalid userid
@ -510,87 +407,6 @@ def test_cancel_subscription_with_invalid_subhub_user(app):
assert cancelled["message"] == "Customer does not exist."
def test_cancel_subscription_with_invalid_stripe_customer(
app, create_subscription_for_processing
):
"""
GIVEN an userid and subscription id
WHEN the user has an invalid stripe customer id
THEN a StripeError is raised
"""
subscription, code = create_subscription_for_processing
subhub_user = g.subhub_account.get_user("process_test")
subhub_user.cust_id = None
g.subhub_account.save_user(subhub_user)
exception = None
try:
cancelled, code = payments.cancel_subscription(
"process_test", subscription["subscriptions"][0]["subscription_id"]
)
except Exception as e:
exception = e
g.subhub_account.remove_from_db("process_test")
assert isinstance(exception, InvalidRequestError)
assert "Customer instance has invalid ID" in exception.user_message
def test_check_subscription_with_valid_parameters(
app, create_subscription_for_processing
):
"""
GIVEN should get a list of active subscriptions
WHEN provided an api_token and a userid id
THEN validate should return list of active subscriptions
"""
subscription, code = create_subscription_for_processing
sub_status, code = payments.subscription_status("process_test")
assert 200 == code
assert len(sub_status) > 0
g.subhub_account.remove_from_db("process_test")
def test_update_payment_method_valid_parameters(
app, create_subscription_for_processing
):
"""
GIVEN api_token, userid, pmt_token
WHEN all parameters are valid
THEN update payment method for a customer
"""
subscription, code = create_subscription_for_processing
updated_pmt, code = payments.update_payment_method(
"process_test", {"pmt_token": "tok_mastercard"}
)
assert 201 == code
g.subhub_account.remove_from_db("process_test")
def test_update_payment_method_invalid_payment_token(
app, create_subscription_for_processing
):
"""
GIVEN api_token, userid, pmt_token
WHEN invalid pmt_token
THEN a StripeError exception is raised
"""
exception = None
try:
updated_pmt, code = payments.update_payment_method(
"process_test", {"pmt_token": "tok_invalid"}
)
except Exception as e:
exception = e
g.subhub_account.remove_from_db("process_test")
assert isinstance(exception, InvalidRequestError)
assert "No such token:" in exception.user_message
def test_update_payment_method_missing_stripe_customer(monkeypatch):
"""
GIVEN api_token, userid, pmt_token

Просмотреть файл

@ -3,7 +3,7 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
import json
from unittest.mock import Mock, patch
from unittest.mock import Mock, MagicMock
import mock
import connexion
@ -12,6 +12,10 @@ import stripe.error
from subhub.app import create_app
from subhub.tests.unit.stripe.utils import MockSubhubUser
from subhub.log import get_logger
logger = get_logger()
class MockCustomer:
id = 123
@ -37,6 +41,53 @@ def test_subhub():
assert isinstance(app, connexion.FlaskApp)
def test_list_plans(app, monkeypatch):
"""
GIVEN a valid token
WHEN a request for plans is made
THEN a success status of 200 is returned
"""
client = app.app.test_client()
plans_data = [
{
"id": "plan_1",
"product": "prod_1",
"interval": "month",
"amount": 25,
"currency": "usd",
"nickname": "Plan 1",
},
{
"id": "plan_2",
"product": "prod_1",
"interval": "year",
"amount": 250,
"currency": "usd",
"nickname": "Plan 2",
},
]
product_data = {"name": "Product 1"}
plans = Mock(return_value=plans_data)
product = Mock(return_value=product_data)
monkeypatch.setattr("stripe.Plan.list", plans)
monkeypatch.setattr("stripe.Product.retrieve", product)
path = "v1/plans"
response = client.get(
path,
headers={"Authorization": "fake_payment_api_key"},
content_type="application/json",
)
assert response.status_code == 200
def test_update_customer_payment_server_stripe_error_with_params(app, monkeypatch):
"""
GIVEN the route POST v1/customer/{id} is called
@ -115,6 +166,94 @@ def test_customer_signup_server_stripe_error_with_params(app, monkeypatch):
assert "No such plan" in loaded_data["message"]
def test_subscribe_success(app, monkeypatch):
"""
GIVEN a route that attempts to make a subscribe a customer
WHEN valid data is provided
THEN a success status of 201 will be returned
"""
client = app.app.test_client()
subscription_data = {
"id": "sub_123",
"status": "active",
"current_period_end": 1566833524,
"current_period_start": 1564155124,
"ended_at": None,
"plan": {"id": "plan_123", "nickname": "Monthly VPN Subscription"},
"cancel_at_period_end": False,
}
mock_false = Mock(return_value=False)
customer = Mock(return_value=MockCustomer())
customer_updated = MagicMock(
return_value={"id": "cust_123", "subscriptions": {"data": [subscription_data]}}
)
create = Mock(return_value={"id": "sub_234"})
user = Mock(return_value=MockSubhubUser())
monkeypatch.setattr("flask.g.subhub_account.get_user", user)
monkeypatch.setattr("stripe.Customer.retrieve", customer_updated)
monkeypatch.setattr("subhub.sub.payments.has_existing_plan", mock_false)
monkeypatch.setattr("subhub.sub.payments.existing_or_new_customer", customer)
monkeypatch.setattr("stripe.Subscription.create", create)
path = "v1/customer/subtest/subscriptions"
data = {
"pmt_token": "tok_visa",
"plan_id": "plan",
"origin_system": "fake_origin1",
"email": "subtest@example.com",
"display_name": "John Tester",
}
response = client.post(
path,
headers={"Authorization": "fake_payment_api_key"},
data=json.dumps(data),
content_type="application/json",
)
logger.info("response data", data=response.data)
assert response.status_code == 201
def test_subscribe_customer_existing(app, monkeypatch):
"""
GIVEN a route that attempts to make a subscribe a customer
WHEN the customer already exists
THEN an error status of 409 will be returned
"""
client = app.app.test_client()
mock_true = Mock(return_value=True)
monkeypatch.setattr("subhub.sub.payments.has_existing_plan", mock_true)
path = "v1/customer/subtest/subscriptions"
data = {
"pmt_token": "tok_visa",
"plan_id": "plan",
"origin_system": "Test_system",
"email": "subtest@example.com",
"display_name": "John Tester",
}
response = client.post(
path,
headers={"Authorization": "fake_payment_api_key"},
data=json.dumps(data),
content_type="application/json",
)
assert response.status_code == 409
def test_subscribe_card_declined_error_handler(app, monkeypatch):
"""
GIVEN a route that attempts to make a stripe payment

107
yarn.lock
Просмотреть файл

@ -451,7 +451,12 @@ ansi-escapes@^1.1.0:
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
integrity sha1-06ioOzGapneTZisT52HHkRQiMG4=
ansi-escapes@^4.2.0, ansi-escapes@^4.2.1:
ansi-escapes@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
ansi-escapes@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228"
integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==
@ -637,9 +642,9 @@ aws-sdk@^2.177.0, aws-sdk@^2.7.0:
xml2js "0.4.19"
aws-sdk@^2.496.0:
version "2.508.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.508.0.tgz#19eeb9da0df41382d45568367837eeb6e7eab3b5"
integrity sha512-VXMxsuwK31U+N259562VPiwQblwRQFQoVnv0d4Gp4Ji+JimCvV6qF73zobZy7cQK5ZPAB25BK4JVMdTYkpi+vA==
version "2.507.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.507.0.tgz#afb30dd2a0a5166aa7c2f808064f2d03e4dc3619"
integrity sha512-sOtaZONTfUx1jh9HzrWMLwoA2cZK2Xn2RAmGV4Y11NM2qhMePOQ501dhAq/ygKMZRffjw23b8mT1rAaDGTn05g==
dependencies:
buffer "4.9.1"
events "1.1.1"
@ -960,12 +965,12 @@ cli-cursor@^1.0.1:
dependencies:
restore-cursor "^1.0.1"
cli-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
dependencies:
restore-cursor "^3.1.0"
restore-cursor "^2.0.0"
cli-width@^2.0.0:
version "2.2.0"
@ -1445,11 +1450,6 @@ emoji-regex@^7.0.1:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -1749,6 +1749,13 @@ figures@^1.3.5:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
dependencies:
escape-string-regexp "^1.0.5"
figures@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9"
@ -2289,21 +2296,21 @@ inquirer@^1.0.2:
through "^2.3.6"
inquirer@^6.5.0:
version "6.5.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.1.tgz#8bfb7a5ac02dac6ff641ac4c5ff17da112fcdb42"
integrity sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==
version "6.5.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42"
integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==
dependencies:
ansi-escapes "^4.2.1"
ansi-escapes "^3.2.0"
chalk "^2.4.2"
cli-cursor "^3.1.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.15"
mute-stream "0.0.8"
figures "^2.0.0"
lodash "^4.17.12"
mute-stream "0.0.7"
run-async "^2.2.0"
rxjs "^6.4.0"
string-width "^4.1.0"
string-width "^2.1.0"
strip-ansi "^5.1.0"
through "^2.3.6"
@ -2426,11 +2433,6 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-installed-globally@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
@ -2927,7 +2929,7 @@ lodash.values@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347"
integrity sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=
lodash@4.17.x, lodash@^4.17.15:
lodash@4.17.x, lodash@^4.17.12, lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@ -3054,10 +3056,10 @@ mime@1.6.0, mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-fn@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
@ -3136,10 +3138,10 @@ mute-stream@0.0.6:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
integrity sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=
mute-stream@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
nan@~2.12.1:
version "2.12.1"
@ -3376,12 +3378,12 @@ onetime@^1.0.0:
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=
onetime@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
dependencies:
mimic-fn "^2.1.0"
mimic-fn "^1.0.0"
opn@^5.5.0:
version "5.5.0"
@ -3769,12 +3771,12 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0"
onetime "^1.0.0"
restore-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
dependencies:
onetime "^5.1.0"
onetime "^2.0.0"
signal-exit "^3.0.2"
ret@~0.1.10:
@ -4203,7 +4205,7 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@ -4220,15 +4222,6 @@ string-width@^3.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff"
integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^5.2.0"
string_decoder@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"