1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 from django.contrib.auth.models import User
6 from mock import MagicMock, Mock, patch, DEFAULT
7 from catalogue.test_utils import WLTestCase
8 from .models import BillingAgreement, BillingPlan
9 from .rest import user_is_subscribed
10 from paypalrestsdk import ResourceNotFound
13 BillingPlanMock = Mock(
15 id='some-billing-plan-id'
19 BillingAgreementMock = Mock(
20 # BillingAgreement() has a .links[]
21 return_value=MagicMock(
25 href="http://paypal.test/approval/"
29 # BillingAgreement.execute(token)
33 id='some-billing-agreement-id',
37 amount={'value': '100'}
43 # Later we can BillingAgreement.find(...).state == 'Active'
52 class PaypalTests(WLTestCase):
55 cls.user = User(username='test')
56 cls.user.set_password('test')
60 def tearDownClass(cls):
63 def test_paypal_form(self):
64 response = self.client.get('/paypal/form/')
65 self.assertEqual(response.status_code, 200)
67 def test_paypal_form_unauthorized(self):
68 """Legacy flow: only allow payment for logged-in users."""
69 response = self.client.post('/paypal/form/', {"amount": "0"})
70 self.assertEqual(response.status_code, 403)
72 def test_paypal_form_invalid(self):
73 """Paypal form: error on bad input."""
74 self.client.login(username='test', password='test')
76 response = self.client.post('/paypal/form/', {"amount": "0"})
77 self.assertEqual(response.status_code, 200)
79 len(response.context['form'].errors['amount']),
82 @patch.multiple('paypalrestsdk',
83 BillingPlan=BillingPlanMock,
84 BillingAgreement=BillingAgreementMock,
86 def test_paypal_form_valid(self):
87 """PayPal form created a BillingPlan."""
88 self.client.login(username='test', password='test')
89 response = self.client.post('/paypal/form/', {"amount": "100"})
90 self.assertRedirects(response, 'http://paypal.test/approval/',
91 fetch_redirect_response=False)
92 self.assertEqual(BillingPlan.objects.all().count(), 1)
94 # Posting the form a second time does not create another plan.
95 response = self.client.post('/paypal/form/', {"amount": "100"})
96 self.assertRedirects(response, 'http://paypal.test/approval/',
97 fetch_redirect_response=False)
98 self.assertEqual(BillingPlan.objects.all().count(), 1)
100 # No BillingAgreement created in our DB yet.
101 self.assertEqual(BillingAgreement.objects.all().count(), 0)
103 @patch('paypalrestsdk.BillingPlan', BillingPlanMock)
104 def test_paypal_form_error(self):
105 """On PayPal error, plan does not get created."""
106 self.client.login(username='test', password='test')
108 # It can choke on BillingPlan().create().
109 with patch('paypalrestsdk.BillingPlan', Mock(
110 return_value=Mock(create=Mock(return_value=None)))):
111 response = self.client.post('/paypal/form/', {"amount": "100"})
112 self.assertEqual(response.status_code, 200)
114 # Or it can choke on BillingPlan().activate().
115 with patch('paypalrestsdk.BillingPlan', Mock(
116 return_value=Mock(activate=Mock(return_value=None)))):
117 response = self.client.post('/paypal/form/', {"amount": "100"})
118 self.assertEqual(response.status_code, 200)
120 # No plan is created yet.
121 self.assertEqual(BillingPlan.objects.all().count(), 0)
123 # Or it can choke later, on BillingAgreement().create()
124 with patch('paypalrestsdk.BillingAgreement', Mock(
125 return_value=Mock(create=Mock(return_value=None)))):
126 response = self.client.post('/paypal/form/', {"amount": "100"})
127 self.assertEqual(response.status_code, 200)
129 # But now the plan should be created.
130 self.assertEqual(BillingPlan.objects.all().count(), 1)
132 @patch.multiple('paypalrestsdk',
133 BillingPlan=BillingPlanMock,
134 BillingAgreement=BillingAgreementMock,
136 def test_paypal_app_form_valid(self):
137 """App form creates a BillingPlan."""
138 self.client.login(username='test', password='test')
139 response = self.client.post('/paypal/app-form/', {"amount": "100"})
140 self.assertRedirects(response, 'http://paypal.test/approval/',
141 fetch_redirect_response=False)
142 self.assertEqual(BillingPlan.objects.all().count(), 1)
144 @patch('paypalrestsdk.BillingAgreement', BillingAgreementMock)
145 def test_paypal_return(self):
146 self.client.login(username='test', password='test')
147 BillingPlan.objects.create(amount=100)
149 # No token = no agreement.
150 response = self.client.get('/paypal/return/')
151 self.assertEqual(response.status_code, 404)
152 self.assertEqual(BillingAgreement.objects.all().count(), 0)
154 response = self.client.get('/paypal/return/?token=secret-token')
155 self.assertEqual(response.status_code, 200)
156 self.assertEqual(BillingAgreement.objects.all().count(), 1)
158 # Repeated returns will not generate further agreements.
159 response = self.client.get('/paypal/return/?token=secret-token')
160 self.assertEqual(response.status_code, 200)
161 self.assertEqual(BillingAgreement.objects.all().count(), 1)
163 self.assertTrue(user_is_subscribed(self.user))
165 @patch('paypalrestsdk.BillingAgreement', BillingAgreementMock)
166 def test_paypal_app_return(self):
167 self.client.login(username='test', password='test')
168 BillingPlan.objects.create(amount=100)
169 response = self.client.get('/paypal/app-return/?token=secret-token')
170 self.assertRedirects(
171 response, 'wolnelekturyapp://paypal_return',
172 fetch_redirect_response=False)
174 # Repeated returns will not generate further agreements.
175 response = self.client.get('/paypal/app-return/?token=secret-token')
176 self.assertRedirects(
177 response, 'wolnelekturyapp://paypal_return',
178 fetch_redirect_response=False)
179 self.assertEqual(BillingAgreement.objects.all().count(), 1)
181 self.assertTrue(user_is_subscribed(self.user))
183 def test_paypal_return_error(self):
184 self.client.login(username='test', password='test')
185 BillingPlan.objects.create(amount=100)
187 # It can choke on BillingAgreement.execute()
188 with patch('paypalrestsdk.BillingAgreement', Mock(
189 execute=Mock(return_value=Mock(id=None)))):
190 self.client.get('/paypal/app-return/?token=secret-token')
191 response = self.client.get('/paypal/app-return/?token=secret-token')
192 self.assertRedirects(
193 response, 'wolnelekturyapp://paypal_error',
194 fetch_redirect_response=False)
196 # No agreement created in our DB if not executed successfully.
197 self.assertEqual(BillingAgreement.objects.all().count(), 0)
199 # It can execute all right, but just not be findable later.
200 with patch('paypalrestsdk.BillingAgreement', Mock(
201 execute=BillingAgreementMock.execute,
202 find=Mock(side_effect=ResourceNotFound(None)))):
203 response = self.client.get('/paypal/app-return/?token=secret-token')
204 self.assertRedirects(
205 response, 'wolnelekturyapp://paypal_return',
206 fetch_redirect_response=False)
208 # Now the agreement exists in our DB, but is not active.
209 self.assertEqual([b.active for b in BillingAgreement.objects.all()], [False])
211 with patch('paypalrestsdk.BillingAgreement', Mock(
212 find=Mock(return_value=Mock(state='Mocked')))):
213 self.assertFalse(user_is_subscribed(self.user))
215 def test_paypal_cancel(self):
216 response = self.client.get('/paypal/cancel/')
217 self.assertEqual(response.status_code, 200)