Skip to content

Commit

Permalink
ChangePlanView tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kavdev committed May 31, 2015
1 parent 0b69bea commit d5886e2
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 17 deletions.
2 changes: 1 addition & 1 deletion HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ History

* Support for Django 1.6 and lower is now deprecated.
* Improved test harness now tests coverage and pep8
* SubscribeFormView no longer populates self.error with form errors
* SubscribeFormView and ChangePlanView no longer populate self.error with form errors

0.5.0 (2015-05-25)
---------------------
Expand Down
2 changes: 1 addition & 1 deletion djstripe/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
PASSWORD_MIN_LENGTH = getattr(settings, 'DJSTRIPE_PASSWORD_MIN_LENGTH', 6)

PRORATION_POLICY = getattr(settings, 'DJSTRIPE_PRORATION_POLICY', False)
PRORATION_POLICY_FOR_UPGRADES = getattr(settings, 'DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES', False)
get_proration_policy_for_upgrades = (lambda: getattr(settings, 'DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES', False))
CANCELLATION_AT_PERIOD_END = not PRORATION_POLICY # TODO - need to find a better way to do this

SEND_INVOICE_RECEIPT_EMAILS = getattr(settings, "DJSTRIPE_SEND_INVOICE_RECEIPT_EMAILS", True)
Expand Down
34 changes: 19 additions & 15 deletions djstripe/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from .settings import PLAN_LIST
from .settings import CANCELLATION_AT_PERIOD_END
from .settings import subscriber_request_callback
from .settings import PRORATION_POLICY_FOR_UPGRADES
from .settings import get_proration_policy_for_upgrades
from .sync import sync_subscriber


Expand Down Expand Up @@ -163,7 +163,11 @@ def post(self, request, *args, **kwargs):


class ChangePlanView(LoginRequiredMixin, FormValidMessageMixin, SubscriptionMixin, FormView):
# TODO - needs tests
"""
TODO: This logic should be in form_valid() instead of post().
Also, this should be combined with SubscribeFormView.
"""

form_class = PlanForm
template_name = "djstripe/subscribe_form.html"
Expand All @@ -172,22 +176,24 @@ class ChangePlanView(LoginRequiredMixin, FormValidMessageMixin, SubscriptionMixi

def post(self, request, *args, **kwargs):
form = PlanForm(request.POST)
customer = subscriber_request_callback(request).customer
try:
customer = subscriber_request_callback(request).customer
except Customer.DoesNotExist as exc:
form.add_error(None, "You must already be subscribed to a plan before you can change it.")
return self.form_invalid(form)

if form.is_valid():
try:
# When a customer upgrades their plan, and PRORATION_POLICY_FOR_UPGRADES is set to True,
# then we force the proration of his current plan and use it towards the upgraded plan,
# no matter what PRORATION_POLICY is set to.
if PRORATION_POLICY_FOR_UPGRADES:
# When a customer upgrades their plan, and DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES is set to True,
# we force the proration of the current plan and use it towards the upgraded plan,
# no matter what DJSTRIPE_PRORATION_POLICY is set to.
if get_proration_policy_for_upgrades():
current_subscription_amount = customer.current_subscription.amount
selected_plan_name = form.cleaned_data["plan"]
selected_plan = next(
(plan for plan in PLAN_LIST if plan["plan"] == selected_plan_name)
)
selected_plan_price = selected_plan["price"]

if not isinstance(selected_plan["price"], decimal.Decimal):
selected_plan_price = selected_plan["price"] / decimal.Decimal("100")
selected_plan_price = selected_plan["price"] / decimal.Decimal("100")

# Is it an upgrade?
if selected_plan_price > current_subscription_amount:
Expand All @@ -196,11 +202,9 @@ def post(self, request, *args, **kwargs):
customer.subscribe(selected_plan_name)
else:
customer.subscribe(form.cleaned_data["plan"])
except stripe.StripeError as e:
self.error = e.message
except stripe.StripeError as exc:
form.add_error(None, str(exc))
return self.form_invalid(form)
except Exception as e:
raise e
return self.form_valid(form)
else:
return self.form_invalid(form)
Expand Down
8 changes: 8 additions & 0 deletions runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@
"currency": "usd",
"interval": "month"
},
"test3": {
"stripe_plan_id": "test_id_3",
"name": "Test Plan 3",
"description": "Test plan for deletion. You'll have to sync plans manually in your test to ensure this plan exists.",
"price": 5000, # $50.00
"currency": "usd",
"interval": "month"
},
"unidentified_test_plan": {
"name": "Unidentified Test Plan",
"description": "A test plan with no ID.",
Expand Down
98 changes: 98 additions & 0 deletions tests/test_integrations/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
from django.core.urlresolvers import reverse
from django.test.client import RequestFactory
from django.test.testcases import TestCase
from django.test.utils import override_settings

from djstripe import settings as djstripe_settings
from djstripe.models import Customer
from djstripe.views import ChangeCardView, HistoryView
from djstripe.sync import sync_plans


if settings.STRIPE_PUBLIC_KEY and settings.STRIPE_SECRET_KEY:
Expand Down Expand Up @@ -219,3 +221,99 @@ def test_post_form_invalid(self):
self.assertEqual(200, response.status_code)
self.assertIn("plan", response.context["form"].errors)
self.assertIn("This field is required.", response.context["form"].errors["plan"])

class ChangePlanViewTest(TestCase):

def setUp(self):
self.url = reverse("djstripe:change_plan")
self.user1 = get_user_model().objects.create_user(username="testuser1",
email="[email protected]",
password="123")
self.user2 = get_user_model().objects.create_user(username="testuser2",
email="[email protected]",
password="123")

token = stripe.Token.create(
card={
"number": '4242424242424242',
"exp_month": 12,
"exp_year": 2016,
"cvc": '123'
},
)

customer, created = Customer.get_or_create(subscriber=self.user1)
customer.update_card(token.id)
customer.subscribe("test")

def test_post_new_sub_no_proration(self):
self.assertTrue(self.client.login(username="testuser2", password="123"))
response = self.client.post(self.url, {"plan": "test0"})
self.assertEqual(200, response.status_code)
self.assertIn("form", response.context)
self.assertIn("You must already be subscribed to a plan before you can change it.", response.context["form"].errors["__all__"])

def test_change_sub_no_proration(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url, {"plan": "test0"})
self.assertRedirects(response, reverse("djstripe:history"))

customer = Customer.objects.get(subscriber=self.user1)
self.assertEqual("test0", customer.current_subscription.plan)
customer.subscribe("test") # revert

def test_post_form_invalid(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url)
self.assertEqual(200, response.status_code)
self.assertIn("plan", response.context["form"].errors)
self.assertIn("This field is required.", response.context["form"].errors["plan"])

@override_settings(DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES=True)
def test_change_sub_with_proration_downgrade(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url, {"plan": "test0"})
self.assertRedirects(response, reverse("djstripe:history"))

customer = Customer.objects.get(subscriber=self.user1)
self.assertEqual("test0", customer.current_subscription.plan)
customer.subscribe("test") # revert

@override_settings(DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES=True)
def test_change_sub_with_proration_upgrade(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url, {"plan": "test2"})
self.assertRedirects(response, reverse("djstripe:history"))

customer = Customer.objects.get(subscriber=self.user1)
self.assertEqual("test2", customer.current_subscription.plan)
customer.subscribe("test") # revert

def test_change_sub_same_plan(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url, {"plan": "test"})
self.assertRedirects(response, reverse("djstripe:history"))

customer = Customer.objects.get(subscriber=self.user1)
self.assertEqual("test", customer.current_subscription.plan)

@override_settings(DJSTRIPE_PRORATION_POLICY_FOR_UPGRADES=True)
def test_change_sub_with_proration_same_plan(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))
response = self.client.post(self.url, {"plan": "test"})
self.assertRedirects(response, reverse("djstripe:history"))

customer = Customer.objects.get(subscriber=self.user1)
self.assertEqual("test", customer.current_subscription.plan)

def test_change_sub_stripe_error(self):
self.assertTrue(self.client.login(username="testuser1", password="123"))

sync_plans()
plan = stripe.Plan.retrieve("test_id_3")
plan.delete()

response = self.client.post(self.url, {"plan": "test3"})
self.assertEqual(200, response.status_code)
self.assertIn("form", response.context)
self.assertIn("No such plan: test_id_3", response.context["form"].errors["__all__"])

0 comments on commit d5886e2

Please sign in to comment.