1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 from django.contrib.auth.models import User
5 from unittest import skip
6 from unittest.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 @skip("Changing the flow.")
64 def test_paypal_form(self):
65 response = self.client.get('/paypal/form/')
66 self.assertEqual(response.status_code, 200)
68 @skip("Changing the flow.")
69 def test_paypal_form_unauthorized(self):
70 """Legacy flow: only allow payment for logged-in users."""
71 response = self.client.post('/paypal/form/', {"amount": "0"})
72 self.assertEqual(response.status_code, 403)
74 @skip("Changing the flow.")
75 def test_paypal_form_invalid(self):
76 """Paypal form: error on bad input."""
77 self.client.login(username='test', password='test')
79 response = self.client.post('/paypal/form/', {"amount": "0"})
80 self.assertEqual(response.status_code, 200)
82 len(response.context['form'].errors['amount']),
85 @skip("Changing the flow.")
86 @patch.multiple('paypalrestsdk',
87 BillingPlan=BillingPlanMock,
88 BillingAgreement=BillingAgreementMock,
90 def test_paypal_form_valid(self):
91 """PayPal form created a BillingPlan."""
92 self.client.login(username='test', password='test')
93 response = self.client.post('/paypal/form/', {"amount": "100"})
94 self.assertRedirects(response, 'http://paypal.test/approval/',
95 fetch_redirect_response=False)
96 self.assertEqual(BillingPlan.objects.all().count(), 1)
98 # Posting the form a second time does not create another plan.
99 response = self.client.post('/paypal/form/', {"amount": "100"})
100 self.assertRedirects(response, 'http://paypal.test/approval/',
101 fetch_redirect_response=False)
102 self.assertEqual(BillingPlan.objects.all().count(), 1)
104 # No BillingAgreement created in our DB yet.
105 self.assertEqual(BillingAgreement.objects.all().count(), 0)
107 @skip("Changing the flow.")
108 @patch('paypalrestsdk.BillingPlan', BillingPlanMock)
109 def test_paypal_form_error(self):
110 """On PayPal error, plan does not get created."""
111 self.client.login(username='test', password='test')
113 # It can choke on BillingPlan().create().
114 with patch('paypalrestsdk.BillingPlan', Mock(
115 return_value=Mock(create=Mock(return_value=None)))):
116 response = self.client.post('/paypal/form/', {"amount": "100"})
117 self.assertEqual(response.status_code, 200)
119 # Or it can choke on BillingPlan().activate().
120 with patch('paypalrestsdk.BillingPlan', Mock(
121 return_value=Mock(activate=Mock(return_value=None)))):
122 response = self.client.post('/paypal/form/', {"amount": "100"})
123 self.assertEqual(response.status_code, 200)
125 # No plan is created yet.
126 self.assertEqual(BillingPlan.objects.all().count(), 0)
128 # Or it can choke later, on BillingAgreement().create()
129 with patch('paypalrestsdk.BillingAgreement', Mock(
130 return_value=Mock(create=Mock(return_value=None)))):
131 response = self.client.post('/paypal/form/', {"amount": "100"})
132 self.assertEqual(response.status_code, 200)
134 # But now the plan should be created.
135 self.assertEqual(BillingPlan.objects.all().count(), 1)
137 @skip("Changing the flow.")
138 @patch.multiple('paypalrestsdk',
139 BillingPlan=BillingPlanMock,
140 BillingAgreement=BillingAgreementMock,
142 def test_paypal_app_form_valid(self):
143 """App form creates a BillingPlan."""
144 self.client.login(username='test', password='test')
145 response = self.client.post('/paypal/app-form/', {"amount": "100"})
146 self.assertRedirects(response, 'http://paypal.test/approval/',
147 fetch_redirect_response=False)
148 self.assertEqual(BillingPlan.objects.all().count(), 1)
150 @patch('paypalrestsdk.BillingAgreement', BillingAgreementMock)
151 def test_paypal_return(self):
152 self.client.login(username='test', password='test')
153 BillingPlan.objects.create(amount=100)
155 # No token = no agreement.
156 response = self.client.get('/paypal/return/')
157 self.assertEqual(response.status_code, 404)
158 self.assertEqual(BillingAgreement.objects.all().count(), 0)
160 response = self.client.get('/paypal/return/?token=secret-token')
161 self.assertEqual(response.status_code, 200)
162 self.assertEqual(BillingAgreement.objects.all().count(), 1)
164 # Repeated returns will not generate further agreements.
165 response = self.client.get('/paypal/return/?token=secret-token')
166 self.assertEqual(response.status_code, 200)
167 self.assertEqual(BillingAgreement.objects.all().count(), 1)
169 self.assertTrue(user_is_subscribed(self.user))
171 @patch('paypalrestsdk.BillingAgreement', BillingAgreementMock)
172 def test_paypal_app_return(self):
173 self.client.login(username='test', password='test')
174 BillingPlan.objects.create(amount=100)
175 response = self.client.get('/paypal/app-return/?token=secret-token')
176 self.assertRedirects(
177 response, 'wolnelekturyapp://paypal_return',
178 fetch_redirect_response=False)
180 # Repeated returns will not generate further agreements.
181 response = self.client.get('/paypal/app-return/?token=secret-token')
182 self.assertRedirects(
183 response, 'wolnelekturyapp://paypal_return',
184 fetch_redirect_response=False)
185 self.assertEqual(BillingAgreement.objects.all().count(), 1)
187 self.assertTrue(user_is_subscribed(self.user))
189 def test_paypal_return_error(self):
190 self.client.login(username='test', password='test')
191 BillingPlan.objects.create(amount=100)
193 # It can choke on BillingAgreement.execute()
194 with patch('paypalrestsdk.BillingAgreement', Mock(
195 execute=Mock(return_value=Mock(id=None)))):
196 self.client.get('/paypal/app-return/?token=secret-token')
197 response = self.client.get('/paypal/app-return/?token=secret-token')
198 self.assertRedirects(
199 response, 'wolnelekturyapp://paypal_error',
200 fetch_redirect_response=False)
202 # No agreement created in our DB if not executed successfully.
203 self.assertEqual(BillingAgreement.objects.all().count(), 0)
205 # It can execute all right, but just not be findable later.
206 with patch('paypalrestsdk.BillingAgreement', Mock(
207 execute=BillingAgreementMock.execute,
208 find=Mock(side_effect=ResourceNotFound(None)))):
209 response = self.client.get('/paypal/app-return/?token=secret-token')
210 self.assertRedirects(
211 response, 'wolnelekturyapp://paypal_return',
212 fetch_redirect_response=False)
214 # Now the agreement exists in our DB, but is not active.
215 self.assertEqual([b.active for b in BillingAgreement.objects.all()], [False])
217 with patch('paypalrestsdk.BillingAgreement', Mock(
218 find=Mock(return_value=Mock(state='Mocked')))):
219 self.assertFalse(user_is_subscribed(self.user))
221 def test_paypal_cancel(self):
222 response = self.client.get('/paypal/cancel/')
223 self.assertEqual(response.status_code, 200)