Skip to content

Commit 5a7f1a5

Browse files
committed
-Añadidos archivos de resultados de cobertura de las extensiones de análisis a gitignore
-Arreglado error en el flujo de reserva y pago -Eliminado boton de 'ver detalle' en la vista principal, ahora el recuadro entero funciona como botón para ver el detalle -Actualizados test de rides, payments y reviews -Añadidas dos actas de runión -Actualizado diagrama de Gantt
1 parent 77fddd5 commit 5a7f1a5

11 files changed

Lines changed: 580 additions & 195 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ media/*
5050
.Spotlight-V100
5151
.Trashes
5252
ehthumbs.db
53-
Thumbs.db
53+
Thumbs.db
54+
.coverage
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
from datetime import timedelta
2+
from unittest.mock import patch
3+
4+
from accounts.models import UserProfile
5+
from django.contrib.auth.models import User
6+
from django.test import Client, TestCase
7+
from django.urls import reverse
8+
from django.utils import timezone
9+
from payments.models import Payment
10+
from payments.tests.test_constants import *
11+
from rides.models import Ride
12+
13+
14+
class PaymentEndpointSecurityTests(TestCase):
15+
"""
16+
Pruebas de seguridad para los endpoints del módulo de pagos.
17+
"""
18+
19+
def setUp(self):
20+
"""
21+
Configura los datos iniciales para las pruebas de seguridad.
22+
"""
23+
print("\nConfigurando pruebas de seguridad para endpoints de pagos...")
24+
25+
self.client = Client()
26+
27+
# Crear usuarios
28+
self.payer = User.objects.create_user(
29+
username=PAYER_USERNAME, email=PAYER_EMAIL, password=PAYER_PASSWORD
30+
)
31+
UserProfile.objects.create(user=self.payer)
32+
33+
self.recipient = User.objects.create_user(
34+
username=RECIPIENT_USERNAME,
35+
email=RECIPIENT_EMAIL,
36+
password=RECIPIENT_PASSWORD,
37+
)
38+
UserProfile.objects.create(user=self.recipient)
39+
40+
self.admin_user = User.objects.create_superuser(
41+
username=ADMIN_USERNAME, email=ADMIN_EMAIL, password=ADMIN_PASSWORD
42+
)
43+
UserProfile.objects.create(user=self.admin_user)
44+
45+
self.other_user = User.objects.create_user(
46+
username="otro_usuario", email="otro@example.com", password="contraseña123"
47+
)
48+
UserProfile.objects.create(user=self.other_user)
49+
50+
# Crear viaje
51+
self.future_date = timezone.now() + timedelta(days=RIDE_DAYS_FUTURE)
52+
self.ride = Ride.objects.create(
53+
driver=self.recipient,
54+
origin=RIDE_ORIGIN,
55+
destination=RIDE_DESTINATION,
56+
departure_time=self.future_date,
57+
price=RIDE_PRICE,
58+
total_seats=4,
59+
)
60+
61+
# Crear pagos
62+
self.pending_payment = Payment.objects.create(
63+
payer=self.payer,
64+
recipient=self.recipient,
65+
amount=PAYMENT_AMOUNT,
66+
ride=self.ride,
67+
status=PAYMENT_STATUS_PENDING,
68+
payment_method=PAYMENT_METHOD_STRIPE,
69+
concept=PAYMENT_CONCEPT,
70+
)
71+
72+
self.completed_payment = Payment.objects.create(
73+
payer=self.payer,
74+
recipient=self.recipient,
75+
amount=PAYMENT_AMOUNT,
76+
ride=self.ride,
77+
status=PAYMENT_STATUS_COMPLETED,
78+
payment_method=PAYMENT_METHOD_CREDIT_CARD,
79+
concept=PAYMENT_CONCEPT,
80+
stripe_payment_intent_id=PAYMENT_STRIPE_ID,
81+
)
82+
83+
print(f"Creados {Payment.objects.count()} pagos para pruebas de seguridad.")
84+
85+
def test_unauthorized_access_to_payment_list(self):
86+
"""
87+
Prueba que un usuario no autenticado no puede acceder a la lista de pagos.
88+
"""
89+
print("\nProbando acceso no autorizado a lista de pagos...")
90+
91+
# Intentar acceder sin autenticación
92+
response = self.client.get(reverse(URL_PAYMENT_LIST))
93+
94+
# Debe redirigir al login
95+
self.assertEqual(response.status_code, 302)
96+
self.assertTrue("/login/" in response.url)
97+
98+
def test_unauthorized_access_to_payment_detail(self):
99+
"""
100+
Prueba que un usuario no autenticado no puede acceder al detalle de un pago.
101+
"""
102+
print("\nProbando acceso no autorizado a detalle de pago...")
103+
104+
# Intentar acceder sin autenticación
105+
response = self.client.get(
106+
reverse(URL_PAYMENT_DETAIL, args=[self.completed_payment.id])
107+
)
108+
109+
# Debe redirigir al login
110+
self.assertEqual(response.status_code, 302)
111+
self.assertTrue("/login/" in response.url)
112+
113+
def test_unauthorized_user_access_to_others_payment(self):
114+
"""
115+
Prueba que un usuario no puede ver pagos de otros usuarios.
116+
"""
117+
print("\nProbando acceso de usuario no autorizado a pago ajeno...")
118+
119+
# Login como otro usuario que no es ni pagador ni receptor
120+
self.client.login(username="otro_usuario", password="contraseña123")
121+
122+
# Intentar ver el detalle de un pago
123+
response = self.client.get(
124+
reverse(URL_PAYMENT_DETAIL, args=[self.completed_payment.id])
125+
)
126+
127+
# Debe redireccionar (acceso denegado)
128+
self.assertEqual(response.status_code, 302)
129+
130+
def test_payer_and_recipient_can_access_payment(self):
131+
"""
132+
Prueba que tanto el pagador como el receptor pueden acceder a sus pagos.
133+
"""
134+
print("\nProbando acceso autorizado a pago...")
135+
136+
# Login como pagador
137+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
138+
139+
# El pagador puede acceder al detalle
140+
response = self.client.get(
141+
reverse(URL_PAYMENT_DETAIL, args=[self.completed_payment.id])
142+
)
143+
self.assertEqual(response.status_code, 200)
144+
145+
self.client.logout()
146+
147+
# Login como receptor
148+
self.client.login(username=RECIPIENT_USERNAME, password=RECIPIENT_PASSWORD)
149+
150+
# El receptor puede acceder al detalle
151+
response = self.client.get(
152+
reverse(URL_PAYMENT_DETAIL, args=[self.completed_payment.id])
153+
)
154+
self.assertEqual(response.status_code, 200)
155+
156+
def test_unauthorized_refund(self):
157+
"""
158+
Prueba que solo el receptor puede reembolsar un pago.
159+
"""
160+
print("\nProbando reembolso no autorizado...")
161+
162+
# Login como pagador
163+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
164+
165+
# Intentar reembolsar
166+
response = self.client.post(
167+
reverse(URL_REFUND_PAYMENT, args=[self.completed_payment.id])
168+
)
169+
170+
# Debe redireccionar (acceso denegado)
171+
self.assertEqual(response.status_code, 302)
172+
173+
# El pago no debe estar reembolsado
174+
self.completed_payment.refresh_from_db()
175+
self.assertNotEqual(self.completed_payment.status, PAYMENT_STATUS_REFUNDED)
176+
177+
def test_unauthorized_cancel(self):
178+
"""
179+
Prueba que solo el pagador puede cancelar un pago pendiente.
180+
"""
181+
print("\nProbando cancelación no autorizada...")
182+
183+
# Login como receptor
184+
self.client.login(username=RECIPIENT_USERNAME, password=RECIPIENT_PASSWORD)
185+
186+
# Intentar cancelar
187+
response = self.client.post(
188+
reverse(URL_CANCEL_PAYMENT, args=[self.pending_payment.id])
189+
)
190+
191+
# Debe redireccionar (acceso denegado)
192+
self.assertEqual(response.status_code, 302)
193+
194+
# El pago no debe estar cancelado
195+
self.pending_payment.refresh_from_db()
196+
self.assertNotEqual(self.pending_payment.status, PAYMENT_STATUS_CANCELLED)
197+
198+
def test_cannot_cancel_completed_payment(self):
199+
"""
200+
Prueba que no se puede cancelar un pago que ya está completado.
201+
"""
202+
print("\nProbando cancelación de pago completado...")
203+
204+
# Login como pagador
205+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
206+
207+
# Intentar cancelar un pago completado
208+
response = self.client.post(
209+
reverse(URL_CANCEL_PAYMENT, args=[self.completed_payment.id])
210+
)
211+
212+
# Debe redireccionar
213+
self.assertEqual(response.status_code, 302)
214+
215+
# El pago no debe estar cancelado
216+
self.completed_payment.refresh_from_db()
217+
self.assertNotEqual(self.completed_payment.status, PAYMENT_STATUS_CANCELLED)
218+
219+
def test_cannot_refund_pending_payment(self):
220+
"""
221+
Prueba que no se puede reembolsar un pago pendiente.
222+
"""
223+
print("\nProbando reembolso de pago pendiente...")
224+
225+
# Login como receptor
226+
self.client.login(username=RECIPIENT_USERNAME, password=RECIPIENT_PASSWORD)
227+
228+
# Intentar reembolsar un pago pendiente
229+
response = self.client.post(
230+
reverse(URL_REFUND_PAYMENT, args=[self.pending_payment.id])
231+
)
232+
233+
# Debe redireccionar
234+
self.assertEqual(response.status_code, 302)
235+
236+
# El pago no debe estar reembolsado
237+
self.pending_payment.refresh_from_db()
238+
self.assertNotEqual(self.pending_payment.status, PAYMENT_STATUS_REFUNDED)
239+
240+
@patch("payments._utils.create_checkout_session")
241+
def test_unauthorized_create_payment_own_ride(self, mock_checkout):
242+
"""
243+
Prueba que un conductor no puede pagar su propio viaje.
244+
"""
245+
print("\nProbando creación de pago para viaje propio...")
246+
247+
# Login como conductor/receptor
248+
self.client.login(username=RECIPIENT_USERNAME, password=RECIPIENT_PASSWORD)
249+
250+
# Intentar crear un pago para su propio viaje
251+
response = self.client.post(
252+
reverse(URL_CREATE_PAYMENT, args=[self.ride.id]),
253+
{"terms_accepted": "on"},
254+
)
255+
256+
# Debe redireccionar
257+
self.assertEqual(response.status_code, 302)
258+
259+
# No debe llamarse a la función de crear sesión de Stripe
260+
mock_checkout.assert_not_called()
261+
262+
def test_csrf_protection_payment_operations(self):
263+
"""
264+
Prueba que las operaciones de pago están protegidas contra CSRF.
265+
"""
266+
print("\nProbando protección CSRF para operaciones de pago...")
267+
268+
# Login como pagador
269+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
270+
271+
# Desactivar verificación CSRF para este cliente
272+
self.client.handler.enforce_csrf_checks = True
273+
274+
# Intentar cancelar sin token CSRF
275+
response = self.client.post(
276+
reverse(URL_CANCEL_PAYMENT, args=[self.pending_payment.id])
277+
)
278+
279+
# Debería fallar por falta de token CSRF
280+
self.assertEqual(response.status_code, 403) # Forbidden
281+
282+
# Login como receptor y probar reembolso sin CSRF
283+
self.client.login(username=RECIPIENT_USERNAME, password=RECIPIENT_PASSWORD)
284+
response = self.client.post(
285+
reverse(URL_REFUND_PAYMENT, args=[self.completed_payment.id])
286+
)
287+
288+
# Debería fallar por falta de token CSRF
289+
self.assertEqual(response.status_code, 403) # Forbidden
290+
291+
def test_nonexistent_payment(self):
292+
"""
293+
Prueba acceder a un pago que no existe.
294+
"""
295+
print("\nProbando acceso a pago inexistente...")
296+
297+
# Login como pagador
298+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
299+
300+
# Intentar acceder a un pago con ID inexistente
301+
response = self.client.get(reverse(URL_PAYMENT_DETAIL, args=[99999]))
302+
303+
# Debería dar error 404
304+
self.assertEqual(response.status_code, 404)
305+
306+
@patch("payments._utils.create_checkout_session")
307+
def test_create_payment_full_ride(self, mock_checkout):
308+
"""
309+
Prueba que no se puede crear un pago para un viaje lleno.
310+
"""
311+
print("\nProbando creación de pago para viaje lleno...")
312+
313+
# Crear un viaje con todos los asientos ocupados
314+
full_ride = Ride.objects.create(
315+
driver=self.recipient,
316+
origin=RIDE_ORIGIN,
317+
destination=RIDE_DESTINATION,
318+
departure_time=self.future_date,
319+
price=RIDE_PRICE,
320+
total_seats=1,
321+
)
322+
full_ride.passengers.add(self.other_user)
323+
324+
# Login como pagador
325+
self.client.login(username=PAYER_USERNAME, password=PAYER_PASSWORD)
326+
327+
# Intentar crear un pago para el viaje lleno
328+
response = self.client.post(
329+
reverse(URL_CREATE_PAYMENT, args=[full_ride.id]),
330+
{"terms_accepted": "on"},
331+
)
332+
333+
# Debe redireccionar
334+
self.assertEqual(response.status_code, 302)
335+
336+
# No debe llamarse a la función de crear sesión de Stripe
337+
mock_checkout.assert_not_called()

0 commit comments

Comments
 (0)