From 2b6d6ed642b4e392245ea99af1197052b887062d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:47:42 +0000 Subject: [PATCH 1/3] Add exhaustive endpoint test suite Agent-Logs-Url: https://github.com/dsaub/proyecto-final/sessions/e596d554-92ce-4f30-95b7-bcb209573c48 Co-authored-by: dsaub <54474838+dsaub@users.noreply.github.com> --- tienda/models.py | 4 +- tienda/tests.py | 614 ++++++++++++++++++++++++++++++++++++++++++++++- tienda/views.py | 14 +- 3 files changed, 622 insertions(+), 10 deletions(-) diff --git a/tienda/models.py b/tienda/models.py index 52004b3..56d986e 100644 --- a/tienda/models.py +++ b/tienda/models.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.db import models from django.contrib.auth.models import User, AbstractUser from django.utils.crypto import get_random_string @@ -314,4 +316,4 @@ class ShippingAddress(models.Model): # Si se marca como predeterminada, desmarcar las demás del usuario if self.is_default: ShippingAddress.objects.filter(user=self.user, is_default=True).update(is_default=False) - super().save(*args, **kwargs) \ No newline at end of file + super().save(*args, **kwargs) diff --git a/tienda/tests.py b/tienda/tests.py index 640d51a..9fdccd5 100644 --- a/tienda/tests.py +++ b/tienda/tests.py @@ -1,7 +1,11 @@ -from django.test import TestCase +import json +from unittest.mock import MagicMock, patch + +from django.test import TestCase, override_settings from django.contrib.auth import get_user_model from django.utils import timezone from django.db import IntegrityError +from django.urls import reverse from datetime import timedelta from .models import ( User, VerificationCode, Category, Image, Product, @@ -1251,4 +1255,610 @@ class ShippingAddressModelTests(TestCase): address_str = str(address) self.assertIn("John Doe", address_str) - self.assertIn("Almería", address_str) \ No newline at end of file + self.assertIn("Almería", address_str) + + +@override_settings( + CACHES={"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}, + SESSION_ENGINE="django.contrib.sessions.backends.db", +) +class EndpointViewTests(TestCase): + @classmethod + def setUpTestData(cls): + cls.password = "StrongPassword123!" + cls.buyer = User.objects.create_user( + username="buyer", + email="buyer@example.com", + password=cls.password, + registration_status=User.RegisterStatus.ACTIVE, + ) + cls.seller = User.objects.create_user( + username="seller", + email="seller@example.com", + password=cls.password, + registration_status=User.RegisterStatus.ACTIVE, + ) + cls.other_user = User.objects.create_user( + username="other", + email="other@example.com", + password=cls.password, + registration_status=User.RegisterStatus.ACTIVE, + ) + cls.category = Category.objects.create(name="Electrónica") + cls.image = Image.objects.create(name="imagen", image="images/test.jpg") + cls.product = Product.objects.create( + name="Producto test", + briefdesc="Breve", + description="Descripción", + price=10.0, + stock=20, + category=cls.category, + primary_image=cls.image, + creator=cls.seller, + ) + cls.address = ShippingAddress.objects.create( + user=cls.buyer, + full_name="Comprador Uno", + address_line_1="Calle Mayor 1", + city="Almería", + postal_code="04001", + phone="600000001", + is_default=True, + ) + cls.other_address = ShippingAddress.objects.create( + user=cls.other_user, + full_name="Otro Usuario", + address_line_1="Calle Otro 2", + city="Almería", + postal_code="04002", + phone="600000002", + ) + + def _login(self, user=None): + self.client.force_login(user or self.buyer) + + def _create_cart_item(self, quantity=1, user=None): + owner = user or self.buyer + cart, _ = Cart.objects.get_or_create(user=owner) + item, _ = CartItem.objects.get_or_create(cart=cart, product=self.product, defaults={"quantity": quantity}) + item.quantity = quantity + item.save() + return item + + def _post_json(self, url_name, data, **kwargs): + return self.client.post( + reverse(url_name, kwargs=kwargs or None), + data=json.dumps(data), + content_type="application/json", + ) + + def test_public_endpoints_render(self): + public_routes = [ + reverse("home"), + reverse("index"), + reverse("productos"), + reverse("producto", args=[self.product.id]), + reverse("categoria", args=[self.category.id]), + reverse("search"), + reverse("search_suggestions"), + reverse("login"), + reverse("register"), + reverse("rgpd"), + reverse("privacidad"), + reverse("devoluciones"), + reverse("aviso_legal"), + reverse("terminos"), + reverse("cookies"), + reverse("sobre_nosotros"), + reverse("ayuda"), + reverse("reset_password"), + ] + for url in public_routes: + with self.subTest(url=url): + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + def test_login_required_endpoints_redirect_anonymous(self): + secured_get_routes = [ + reverse("mis_productos"), + reverse("pedidos_vendedor"), + reverse("crear_producto"), + reverse("checkout"), + reverse("checkout_success"), + reverse("checkout_cancel"), + reverse("portal_usuario"), + reverse("mis_compras"), + reverse("mis_recibos"), + reverse("editar_perfil"), + reverse("direcciones_usuario"), + reverse("crear_direccion"), + reverse("mensajes_comprador"), + reverse("metodos_pago"), + reverse("agregar_tarjeta"), + reverse("agregar_paypal"), + reverse("editar_producto", args=[self.product.id]), + reverse("borrar_producto", args=[self.product.id]), + reverse("cambiar_estado_pedido", args=[1]), + reverse("enviar_mensaje_pedido", args=[1]), + reverse("editar_direccion", args=[self.address.id]), + reverse("eliminar_direccion", args=[self.address.id]), + reverse("eliminar_metodo_pago", args=[1]), + ] + for url in secured_get_routes: + with self.subTest(url=url): + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + self.assertIn(reverse("login"), response.url) + + secured_post_routes = [ + "crear_payment_intent", + "confirmar_pago_tarjeta", + "crear_orden_paypal", + "capturar_orden_paypal", + "crear_setup_intent", + "confirmar_setup_intent", + "crear_orden_paypal_setup", + "capturar_orden_paypal_setup", + ] + for name in secured_post_routes: + with self.subTest(name=name): + response = self.client.post(reverse(name)) + self.assertEqual(response.status_code, 302) + self.assertIn(reverse("login"), response.url) + + @patch("tienda.views.tasks.enviar_correo_confirmacion.delay") + @patch("tienda.views.tasks.enviar_correo_bienvenida.delay") + def test_register_login_logout_and_verify_flows(self, welcome_delay, confirm_delay): + register_response = self.client.post(reverse("register"), data={ + "name": "Nuevo", + "email": "nuevo@example.com", + "password": self.password, + "password_confirm": self.password, + }) + self.assertEqual(register_response.status_code, 302) + confirm_delay.assert_called_once() + + created_user = User.objects.get(email="nuevo@example.com") + created_user.registration_status = User.RegisterStatus.ACTIVE + created_user.save(update_fields=["registration_status"]) + + login_response = self.client.post(reverse("login"), data={ + "email": "nuevo@example.com", + "password": self.password, + "remember": "on", + }) + self.assertEqual(login_response.status_code, 302) + self.assertEqual(login_response.url, reverse("index")) + welcome_delay.assert_called_once() + + logout_response = self.client.get(reverse("logout")) + self.assertEqual(logout_response.status_code, 302) + self.assertEqual(logout_response.url, reverse("index")) + + verification = VerificationCode.generate(created_user, VerificationCode.VerificationModes.VERIFY_ACCOUNT) + verify_response = self.client.get(reverse("verify", args=[verification.code])) + self.assertEqual(verify_response.status_code, 302) + created_user.refresh_from_db() + self.assertEqual(created_user.registration_status, User.RegisterStatus.ACTIVE) + + invalid_verify_response = self.client.get(reverse("verify", args=["codigo-invalido"])) + self.assertEqual(invalid_verify_response.status_code, 200) + + @patch("tienda.views.tasks.enviar_correo_recuperacion.delay") + def test_password_reset_endpoints(self, recovery_delay): + reset_get = self.client.get(reverse("reset_password")) + self.assertEqual(reset_get.status_code, 200) + reset_post = self.client.post(reverse("reset_password"), data={"email": self.buyer.email}) + self.assertEqual(reset_post.status_code, 200) + recovery_delay.assert_called_once_with(self.buyer.email) + + code = VerificationCode.generate(self.buyer, VerificationCode.VerificationModes.RESET_PASSWORD) + phase2_get = self.client.get(reverse("reset_password_phase2", args=[code.code])) + self.assertEqual(phase2_get.status_code, 200) + + mismatch = self.client.post(reverse("reset_password_phase2", args=[code.code]), data={ + "password": "NuevaPassword123!", + "verify_password": "DistintaPassword123!", + }) + self.assertEqual(mismatch.status_code, 200) + + success = self.client.post(reverse("reset_password_phase2", args=[code.code]), data={ + "password": "NuevaPassword123!", + "verify_password": "NuevaPassword123!", + }) + self.assertEqual(success.status_code, 302) + self.assertTrue(User.objects.get(id=self.buyer.id).check_password("NuevaPassword123!")) + + not_found = self.client.get(reverse("reset_password_phase2", args=["no-existe"])) + self.assertEqual(not_found.status_code, 404) + + def test_search_and_suggestions(self): + response = self.client.get(reverse("search"), data={"q": "Producto"}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "Producto test") + + suggestions = self.client.get(reverse("search_suggestions"), data={"q": "Pr"}) + self.assertEqual(suggestions.status_code, 200) + payload = suggestions.json() + self.assertTrue(payload["suggestions"]) + + def test_cart_endpoints(self): + self._login() + cart_view = self.client.get(reverse("view_cart")) + self.assertEqual(cart_view.status_code, 200) + + add_response = self.client.post(reverse("add_to_cart", args=[self.product.id]), data={"quantity": 2}) + self.assertEqual(add_response.status_code, 302) + item = CartItem.objects.get(cart__user=self.buyer, product=self.product) + self.assertEqual(item.quantity, 2) + + update_response = self.client.post(reverse("update_cart_item", args=[item.id]), data={"quantity": 3}) + self.assertEqual(update_response.status_code, 302) + item.refresh_from_db() + self.assertEqual(item.quantity, 3) + + remove_response = self.client.post(reverse("remove_from_cart", args=[item.id])) + self.assertEqual(remove_response.status_code, 302) + self.assertFalse(CartItem.objects.filter(id=item.id).exists()) + + self._create_cart_item(quantity=1) + clear_response = self.client.post(reverse("clear_cart")) + self.assertEqual(clear_response.status_code, 302) + self.assertEqual(CartItem.objects.filter(cart__user=self.buyer).count(), 0) + + def test_seller_panel_endpoints(self): + self._login(self.seller) + order = Order.objects.create( + buyer=self.buyer, + shipping_address=self.address, + total=12.1, + payment_method=Order.PAYMENT_STRIPE, + ) + item = OrderItem.objects.create( + order=order, + product=self.product, + product_name=self.product.name, + seller=self.seller, + quantity=1, + unit_price=12.1, + total_price=12.1, + ) + + self.assertEqual(self.client.get(reverse("mis_productos")).status_code, 200) + self.assertEqual(self.client.get(reverse("pedidos_vendedor")).status_code, 200) + self.assertEqual(self.client.get(reverse("crear_producto")).status_code, 200) + self.assertEqual(self.client.get(reverse("editar_producto", args=[self.product.id])).status_code, 200) + + create_response = self.client.post(reverse("crear_producto"), data={ + "name": "Nuevo producto", + "briefdesc": "Breve", + "description": "Descripción", + "price": "25.50", + "stock": "5", + "category": str(self.category.id), + }) + self.assertEqual(create_response.status_code, 302) + created = Product.objects.get(name="Nuevo producto") + + edit_response = self.client.post(reverse("editar_producto", args=[created.id]), data={ + "name": "Producto editado", + "briefdesc": "Actualizado", + "description": "Descripción nueva", + "price": "30.00", + "stock": "6", + "category": str(self.category.id), + }) + self.assertEqual(edit_response.status_code, 302) + created.refresh_from_db() + self.assertEqual(created.name, "Producto editado") + + status_response = self.client.post(reverse("cambiar_estado_pedido", args=[item.id]), data={ + "estado": OrderItem.STATUS_PROCESSING, + }) + self.assertEqual(status_response.status_code, 302) + item.refresh_from_db() + self.assertEqual(item.status, OrderItem.STATUS_PROCESSING) + + message_response = self.client.post(reverse("enviar_mensaje_pedido", args=[item.id]), data={"mensaje": "Preparando envío"}) + self.assertEqual(message_response.status_code, 302) + self.assertTrue(OrderMessage.objects.filter(order_item=item, sender=self.seller).exists()) + + delete_get = self.client.get(reverse("borrar_producto", args=[created.id])) + self.assertEqual(delete_get.status_code, 302) + delete_post = self.client.post(reverse("borrar_producto", args=[created.id])) + self.assertEqual(delete_post.status_code, 302) + self.assertFalse(Product.objects.filter(id=created.id).exists()) + + @patch("tienda.views.stripe.PaymentIntent.create") + @patch("tienda.views._create_stock_reservation_for_cart") + def test_crear_payment_intent_endpoint(self, reservation_mock, create_pi_mock): + self._login() + self._create_cart_item(quantity=2) + reservation_mock.return_value = (MagicMock(id=321), []) + create_pi_mock.return_value = MagicMock(client_secret="secret", id="pi_123") + + self.assertEqual(self.client.get(reverse("crear_payment_intent")).status_code, 405) + bad_json = self.client.post(reverse("crear_payment_intent"), data="{", content_type="application/json") + self.assertEqual(bad_json.status_code, 400) + + missing_address = self._post_json("crear_payment_intent", {}) + self.assertEqual(missing_address.status_code, 400) + + ok = self._post_json("crear_payment_intent", {"shipping_address_id": self.address.id, "save_card": True}) + self.assertEqual(ok.status_code, 200) + data = ok.json() + self.assertEqual(data["client_secret"], "secret") + self.assertEqual(data["payment_intent_id"], "pi_123") + + @patch("tienda.views.create_order_from_cart") + @patch("tienda.views.stripe.PaymentIntent.retrieve") + def test_confirmar_pago_tarjeta_endpoint(self, retrieve_mock, create_order_mock): + self._login() + self._create_cart_item(quantity=1) + session = self.client.session + session["selected_shipping_address_id"] = self.address.id + session.save() + + retrieve_mock.return_value = MagicMock(status="succeeded") + order = Order.objects.create( + buyer=self.buyer, + shipping_address=self.address, + total=12.1, + payment_method=Order.PAYMENT_STRIPE, + ) + create_order_mock.return_value = (order, "") + + self.assertEqual(self.client.get(reverse("confirmar_pago_tarjeta")).status_code, 405) + self.assertEqual(self.client.post(reverse("confirmar_pago_tarjeta"), data="{", content_type="application/json").status_code, 400) + self.assertEqual(self._post_json("confirmar_pago_tarjeta", {}).status_code, 400) + + ok = self._post_json("confirmar_pago_tarjeta", {"payment_intent_id": "pi_ok"}) + self.assertEqual(ok.status_code, 200) + self.assertTrue(ok.json()["success"]) + + @patch("tienda.views._paypal_create_order") + @patch("tienda.views._create_stock_reservation_for_cart") + def test_crear_orden_paypal_endpoint(self, reservation_mock, create_order_mock): + self._login() + self._create_cart_item(quantity=1) + reservation_mock.return_value = (MagicMock(id=555), []) + create_order_mock.return_value = {"id": "ORDER123"} + + self.assertEqual(self.client.get(reverse("crear_orden_paypal")).status_code, 405) + missing_address = self._post_json("crear_orden_paypal", {}) + self.assertEqual(missing_address.status_code, 400) + + ok = self._post_json("crear_orden_paypal", {"shipping_address_id": self.address.id}) + self.assertEqual(ok.status_code, 200) + self.assertEqual(ok.json()["id"], "ORDER123") + + @patch("tienda.views.create_order_from_cart") + @patch("tienda.views._paypal_capture_order") + def test_capturar_orden_paypal_endpoint(self, capture_mock, create_order_mock): + self._login() + self._create_cart_item(quantity=1) + session = self.client.session + session["paypal_order_id"] = "ORDER123" + session["selected_shipping_address_id"] = self.address.id + session.save() + + order = Order.objects.create( + buyer=self.buyer, + shipping_address=self.address, + total=12.1, + payment_method=Order.PAYMENT_PAYPAL, + ) + create_order_mock.return_value = (order, "") + + self.assertEqual(self.client.get(reverse("capturar_orden_paypal")).status_code, 405) + self.assertEqual(self.client.post(reverse("capturar_orden_paypal"), data="{", content_type="application/json").status_code, 400) + self.assertEqual(self._post_json("capturar_orden_paypal", {}).status_code, 400) + self.assertEqual(self._post_json("capturar_orden_paypal", {"orderID": "WRONG"}).status_code, 400) + + capture_mock.return_value = {"status": "APPROVED"} + not_completed = self._post_json("capturar_orden_paypal", {"orderID": "ORDER123"}) + self.assertEqual(not_completed.status_code, 400) + + capture_mock.return_value = { + "status": "COMPLETED", + "payer": {"email_address": "paypal@example.com", "payer_id": "payer_123"}, + } + ok = self._post_json("capturar_orden_paypal", {"orderID": "ORDER123", "save_paypal": True}) + self.assertEqual(ok.status_code, 200) + self.assertTrue(ok.json()["success"]) + self.assertTrue(SavedPaymentMethod.objects.filter(user=self.buyer, paypal_email="paypal@example.com").exists()) + + @patch("tienda.views.create_order_from_cart") + @patch("paypalrestsdk.Payment.find") + @patch("paypalrestsdk.configure") + def test_paypal_legacy_endpoints(self, configure_mock, find_mock, create_order_mock): + self._login() + self._create_cart_item(quantity=1) + + self.assertEqual(self.client.get(reverse("create_paypal_payment")).status_code, 405) + + with patch("paypalrestsdk.Payment") as payment_cls, patch("tienda.views._create_stock_reservation_for_cart") as reservation_mock: + reservation_mock.return_value = (MagicMock(id=777), []) + payment_instance = MagicMock() + payment_instance.create.return_value = True + payment_instance.id = "PAY-123" + payment_instance.links = [MagicMock(rel="approval_url", href="https://paypal.local/approve")] + payment_cls.return_value = payment_instance + create_payment = self.client.post(reverse("create_paypal_payment"), data={"shipping_address_id": self.address.id}) + self.assertEqual(create_payment.status_code, 200) + self.assertIn("redirect", create_payment.json()) + + missing_data = self.client.get(reverse("paypal_execute")) + self.assertEqual(missing_data.status_code, 302) + + session = self.client.session + session["paypal_payment_id"] = "PAY-123" + session["selected_shipping_address_id"] = self.address.id + session.save() + payment_found = MagicMock() + payment_found.execute.return_value = True + find_mock.return_value = payment_found + order = Order.objects.create( + buyer=self.buyer, + shipping_address=self.address, + total=12.1, + payment_method=Order.PAYMENT_PAYPAL, + ) + create_order_mock.return_value = (order, "") + execute = self.client.get(reverse("paypal_execute"), data={"PayerID": "payer"}) + self.assertEqual(execute.status_code, 200) + + @patch("tienda.views.stripe.SetupIntent.create") + @patch("tienda.views._get_or_create_stripe_customer") + def test_setup_intent_endpoints(self, customer_mock, setup_mock): + self._login() + self.assertEqual(self.client.get(reverse("metodos_pago")).status_code, 200) + self.assertEqual(self.client.get(reverse("agregar_tarjeta")).status_code, 200) + self.assertEqual(self.client.get(reverse("agregar_paypal")).status_code, 200) + + self.assertEqual(self.client.get(reverse("crear_setup_intent")).status_code, 405) + customer_mock.return_value = "cus_123" + setup_mock.return_value = MagicMock(client_secret="seti_secret") + setup_response = self.client.post(reverse("crear_setup_intent")) + self.assertEqual(setup_response.status_code, 200) + self.assertEqual(setup_response.json()["customer_id"], "cus_123") + + self.assertEqual(self.client.get(reverse("confirmar_setup_intent")).status_code, 405) + self.assertEqual(self.client.post(reverse("confirmar_setup_intent"), data="{", content_type="application/json").status_code, 400) + self.assertEqual(self._post_json("confirmar_setup_intent", {}).status_code, 400) + + with patch("tienda.views.stripe.PaymentMethod.attach"), patch("tienda.views.stripe.PaymentMethod.retrieve") as retrieve_pm: + retrieve_pm.return_value = MagicMock( + card=MagicMock(brand="visa", last4="4242", exp_month=1, exp_year=2030) + ) + confirm = self._post_json("confirmar_setup_intent", {"payment_method_id": "pm_123"}) + self.assertEqual(confirm.status_code, 200) + self.assertTrue(confirm.json()["success"]) + + @patch("tienda.views._paypal_create_order") + def test_paypal_setup_endpoints(self, create_order_mock): + self._login() + self.assertEqual(self.client.get(reverse("crear_orden_paypal_setup")).status_code, 405) + create_order_mock.return_value = {"id": "ORDER_SETUP"} + create_response = self.client.post(reverse("crear_orden_paypal_setup")) + self.assertEqual(create_response.status_code, 200) + self.assertEqual(create_response.json()["id"], "ORDER_SETUP") + + self.assertEqual(self.client.get(reverse("capturar_orden_paypal_setup")).status_code, 405) + self.assertEqual(self.client.post(reverse("capturar_orden_paypal_setup"), data="{", content_type="application/json").status_code, 400) + self.assertEqual(self._post_json("capturar_orden_paypal_setup", {}).status_code, 400) + + with patch("tienda.views._paypal_capture_order") as capture_mock: + capture_mock.return_value = {"status": "COMPLETED", "payer": {"email_address": "payer@example.com", "payer_id": "payer_1"}} + capture_response = self._post_json("capturar_orden_paypal_setup", {"orderID": "ORDER_SETUP"}) + self.assertEqual(capture_response.status_code, 200) + self.assertTrue(capture_response.json()["success"]) + + def test_user_portal_and_addresses_endpoints(self): + self._login() + order = Order.objects.create( + buyer=self.buyer, + shipping_address=self.address, + total=12.1, + payment_method=Order.PAYMENT_STRIPE, + ) + item = OrderItem.objects.create( + order=order, + product=self.product, + product_name=self.product.name, + seller=self.seller, + quantity=1, + unit_price=12.1, + total_price=12.1, + ) + OrderMessage.objects.create(order_item=item, sender=self.seller, message="Mensaje vendedor") + + self.assertEqual(self.client.get(reverse("portal_usuario")).status_code, 200) + self.assertEqual(self.client.get(reverse("mis_compras")).status_code, 200) + self.assertEqual(self.client.get(reverse("mis_recibos")).status_code, 200) + self.assertEqual(self.client.get(reverse("mensajes_comprador")).status_code, 200) + self.assertEqual(self.client.get(reverse("direcciones_usuario")).status_code, 200) + self.assertEqual(self.client.get(reverse("crear_direccion")).status_code, 200) + self.assertEqual(self.client.get(reverse("editar_direccion", args=[self.address.id])).status_code, 200) + + profile = self.client.post(reverse("editar_perfil"), data={ + "first_name": "Nombre", + "last_name": "Apellido", + "email": "buyer-updated@example.com", + }) + self.assertEqual(profile.status_code, 302) + self.buyer.refresh_from_db() + self.assertEqual(self.buyer.email, "buyer-updated@example.com") + + wrong_current = self.client.post(reverse("cambiar_contrasena"), data={ + "current_password": "incorrecta", + "new_password": "PasswordNueva123!", + "confirm_password": "PasswordNueva123!", + }) + self.assertEqual(wrong_current.status_code, 200) + changed = self.client.post(reverse("cambiar_contrasena"), data={ + "current_password": self.password, + "new_password": "PasswordNueva123!", + "confirm_password": "PasswordNueva123!", + }) + self.assertEqual(changed.status_code, 302) + + invalid_city = self.client.post(reverse("crear_direccion"), data={ + "full_name": "Comprador Uno", + "address_line_1": "Calle Nueva 3", + "city": "Madrid", + "postal_code": "04003", + "phone": "600000003", + }) + self.assertEqual(invalid_city.status_code, 200) + invalid_postal = self.client.post(reverse("crear_direccion"), data={ + "full_name": "Comprador Uno", + "address_line_1": "Calle Nueva 3", + "city": "Almería", + "postal_code": "28001", + "phone": "600000003", + }) + self.assertEqual(invalid_postal.status_code, 200) + + create_ok = self.client.post(reverse("crear_direccion"), data={ + "full_name": "Comprador Dos", + "address_line_1": "Calle Nueva 3", + "city": "Almería", + "postal_code": "04003", + "phone": "600000003", + "is_default": "on", + }) + self.assertEqual(create_ok.status_code, 302) + new_address = ShippingAddress.objects.get(full_name="Comprador Dos") + self.assertEqual(new_address.country, "España") + + edit_ok = self.client.post(reverse("editar_direccion", args=[new_address.id]), data={ + "full_name": "Comprador Dos Editado", + "address_line_1": "Calle Editada 9", + "city": "Almería", + "postal_code": "04004", + "phone": "600000004", + }) + self.assertEqual(edit_ok.status_code, 302) + new_address.refresh_from_db() + self.assertEqual(new_address.full_name, "Comprador Dos Editado") + + delete_get = self.client.get(reverse("eliminar_direccion", args=[new_address.id])) + self.assertEqual(delete_get.status_code, 302) + delete_post = self.client.post(reverse("eliminar_direccion", args=[new_address.id])) + self.assertEqual(delete_post.status_code, 302) + self.assertFalse(ShippingAddress.objects.filter(id=new_address.id).exists()) + + def test_delete_payment_method_endpoint(self): + self._login() + method = SavedPaymentMethod.objects.create( + user=self.buyer, + method_type=SavedPaymentMethod.TYPE_CARD, + label="Visa 4242", + stripe_payment_method_id="pm_4242", + ) + self.assertEqual(self.client.get(reverse("eliminar_metodo_pago", args=[method.id])).status_code, 302) + with patch("tienda.views.stripe.PaymentMethod.detach"): + response = self.client.post(reverse("eliminar_metodo_pago", args=[method.id])) + self.assertEqual(response.status_code, 302) + self.assertFalse(SavedPaymentMethod.objects.filter(id=method.id).exists()) diff --git a/tienda/views.py b/tienda/views.py index 4b783b7..a44db6b 100644 --- a/tienda/views.py +++ b/tienda/views.py @@ -1009,13 +1009,6 @@ def editar_producto(request: HttpRequest, id: int): primary_image_file = request.FILES.get("primary_image") secondary_images_files = request.FILES.getlist("secondary_images") - if stock > 4294967295: - messages.error(request, "No se puede tener mas de 4294967295 de stock.") - categories = Category.objects.all() - return render(request, "tienda/editar_producto.html", { - "categories": categories, - "producto": producto - }) if not all([name, description, price, stock, category_id]): messages.error(request, "Por favor completa todos los campos obligatorios.") categories = Category.objects.all() @@ -1040,6 +1033,13 @@ def editar_producto(request: HttpRequest, id: int): stock = int(stock) if stock < 0: raise ValueError("El stock no puede ser negativo") + if stock > 4294967295: + messages.error(request, "No se puede tener mas de 4294967295 de stock.") + categories = Category.objects.all() + return render(request, "tienda/editar_producto.html", { + "categories": categories, + "producto": producto + }) except ValueError: messages.error(request, "El stock debe ser un número entero válido.") categories = Category.objects.all() From 668ac1a1296c411280f55959ecf390dbc29912a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:49:20 +0000 Subject: [PATCH 2/3] Fix legal/help template inheritance order Agent-Logs-Url: https://github.com/dsaub/proyecto-final/sessions/e596d554-92ce-4f30-95b7-bcb209573c48 Co-authored-by: dsaub <54474838+dsaub@users.noreply.github.com> --- .../__pycache__/0001_initial.cpython-312.pyc | Bin 12957 -> 12957 bytes ...ioncode_code_mode_and_more.cpython-312.pyc | Bin 1028 -> 1028 bytes ...003_order_transaction_code.cpython-312.pyc | Bin 2006 -> 2006 bytes ...ation_stockreservationitem.cpython-312.pyc | Bin 3069 -> 3069 bytes .../0005_savedpaymentmethod.cpython-312.pyc | Bin 2338 -> 2338 bytes .../0006_alter_category_name.cpython-312.pyc | Bin 0 -> 768 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 174 -> 174 bytes tienda/templates/tienda/aviso_legal.html | 2 +- tienda/templates/tienda/ayuda.html | 2 +- tienda/templates/tienda/cookies.html | 2 +- tienda/templates/tienda/devoluciones.html | 2 +- tienda/templates/tienda/rgpd.html | 4 ++-- tienda/templates/tienda/sobre_nosotros.html | 2 +- tienda/templates/tienda/terminos.html | 2 +- .../__pycache__/__init__.cpython-312.pyc | Bin 176 -> 176 bytes .../__pycache__/vat_filters.cpython-312.pyc | Bin 1470 -> 1470 bytes 16 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc diff --git a/tienda/migrations/__pycache__/0001_initial.cpython-312.pyc b/tienda/migrations/__pycache__/0001_initial.cpython-312.pyc index b63c14470c6fd53c132dc1f5bbc0e11184beb32b..9c5969213b1d88cb1648add5594aee554d5bb796 100644 GIT binary patch delta 20 acmbQ6IyaU3G%qg~0}$-F@pL2iBqIPu>IV1# delta 20 acmbQ6IyaU3G%qg~0}w<^zOj*ek`VwxPXK&CAQh00et(Jl)9slNkUuzXn7A delta 20 acmZqSXyM>K&CAQh00a?}Z*1iL$qWE8Bn6HD diff --git a/tienda/migrations/__pycache__/0003_order_transaction_code.cpython-312.pyc b/tienda/migrations/__pycache__/0003_order_transaction_code.cpython-312.pyc index a1ecd9ebf75bb469ac93d41fafbf617ebdac7b38..b345b464876784adc11e0cdca842300ac0673ba1 100644 GIT binary patch delta 20 acmcb{e~q8}G%qg~0}$-F@pL2iX?6fZj|R*D delta 20 acmcb{e~q8}G%qg~0}w<^zOj+}G&=x3^ad6H diff --git a/tienda/migrations/__pycache__/0004_product_stock_stockreservation_stockreservationitem.cpython-312.pyc b/tienda/migrations/__pycache__/0004_product_stock_stockreservation_stockreservationitem.cpython-312.pyc index 93a7921553b92632f6bc66d3930e78c177795531..16b79e2a4320788f825693a7ea7444180324455d 100644 GIT binary patch delta 20 acmew>{#Ts)G%qg~0}$-F@pL2iCvE^r%Li2e delta 20 acmew>{#Ts)G%qg~0}w<^zOj+}6E^@xFb1Ch diff --git a/tienda/migrations/__pycache__/0005_savedpaymentmethod.cpython-312.pyc b/tienda/migrations/__pycache__/0005_savedpaymentmethod.cpython-312.pyc index 3fc5dc13658fe965a9c6f2023df12e6e0f07a87a..924cd09df61a59aafc2e71e3d504a36e7fb4206b 100644 GIT binary patch delta 20 acmZ1^v`C2iG%qg~0}$-F@pL1%I41x)-37P+ delta 20 acmZ1^v`C2iG%qg~0}yCWxv`O3oD%>w4FuKz diff --git a/tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc b/tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0306e68f36756469dc5334c725a410e5a8b82fca GIT binary patch literal 768 zcmZ8f&ubGw6n?Y2neHyF)F4FB3Wgx$U^W*$Ns9;yqCI(9kYSpcCL8xhI=fN#rqF{o zd+WcTw10pH{{X>LKuDOYc<3#ViqMlYyIG>n;qCX$y!Yn&-t4!H4Hwk(vW!#5BZ@EDkJ2+ZVYh^wO2_rxr*Z{}9JcBV<&kZu(812F?ssXTvq^1K(h%k9^YrZVd~;f0uxg!Sop(Uix=mGiI^cDLygE zlhd_Img;BccH5E8vr3VYhqHjkjLJs0+r3Lt`iip=%_AOX5zht*lXj10yq}0%S}~3I z*;m!~dEtw`At8_Z*+AlP92|^!+f;6~L!vW<)-7xM0T0=(y4EJeHX+g_M5T>GuJAe` z2V)xQ0?im7DLFju1w0jcp3;2~W?VedU4*4KoQDHia_J@`zMSnRSg1p|c;O9_hdlY6&#mwEo%1!zapxwk<9AQQ z)vDue+ma3&(zu^=*j{12FkIf*&}l{PHH9wh`{e|;#dSs1j|}Mz73L;F=r;z`KF8|_ RAGy=b)?~Bw3lu4;{{Z$P#BKlp literal 0 HcmV?d00001 diff --git a/tienda/migrations/__pycache__/__init__.cpython-312.pyc b/tienda/migrations/__pycache__/__init__.cpython-312.pyc index 7168695ad8268f2d5dc8b0cd946e0348d4794a9b..f910764f4854a0d0eeec43328d05b0480d1fc2d9 100644 GIT binary patch delta 19 ZcmZ3-xQ>zgG%qg~0}$-F@pK~hVgNM11`z-N delta 19 ZcmZ3-xQ>zgG%qg~0}w<^zA=$|F#s^y1zP|B diff --git a/tienda/templates/tienda/aviso_legal.html b/tienda/templates/tienda/aviso_legal.html index a3fc895..a847648 100644 --- a/tienda/templates/tienda/aviso_legal.html +++ b/tienda/templates/tienda/aviso_legal.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templates/tienda/ayuda.html b/tienda/templates/tienda/ayuda.html index 97582ac..0d336c5 100644 --- a/tienda/templates/tienda/ayuda.html +++ b/tienda/templates/tienda/ayuda.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templates/tienda/cookies.html b/tienda/templates/tienda/cookies.html index 17fa473..3fdee13 100644 --- a/tienda/templates/tienda/cookies.html +++ b/tienda/templates/tienda/cookies.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templates/tienda/devoluciones.html b/tienda/templates/tienda/devoluciones.html index c64ffbd..613c82e 100644 --- a/tienda/templates/tienda/devoluciones.html +++ b/tienda/templates/tienda/devoluciones.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templates/tienda/rgpd.html b/tienda/templates/tienda/rgpd.html index 09546c1..37ff7bb 100644 --- a/tienda/templates/tienda/rgpd.html +++ b/tienda/templates/tienda/rgpd.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
@@ -61,4 +61,4 @@
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/tienda/templates/tienda/sobre_nosotros.html b/tienda/templates/tienda/sobre_nosotros.html index 8120358..5ce9573 100644 --- a/tienda/templates/tienda/sobre_nosotros.html +++ b/tienda/templates/tienda/sobre_nosotros.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templates/tienda/terminos.html b/tienda/templates/tienda/terminos.html index 5a47506..31a36d6 100644 --- a/tienda/templates/tienda/terminos.html +++ b/tienda/templates/tienda/terminos.html @@ -1,5 +1,5 @@ -{% load static %} {% extends "tienda/base.html" %} +{% load static %} {% block content %}
diff --git a/tienda/templatetags/__pycache__/__init__.cpython-312.pyc b/tienda/templatetags/__pycache__/__init__.cpython-312.pyc index 2dd2093c22e5192730d561cdd41e7088cbdea917..6a9d06aa8c65713800d5d07db1982561ad1b5c22 100644 GIT binary patch delta 19 ZcmdnMxPg)TG%qg~0}$-F@pK~hQUEmx1{VMT delta 19 ZcmdnMxPg)TG%qg~0}w<`zA=$|DF87l1!4dI diff --git a/tienda/templatetags/__pycache__/vat_filters.cpython-312.pyc b/tienda/templatetags/__pycache__/vat_filters.cpython-312.pyc index f30aca6297b116197c16520b77b8226b56a29b8c..3872719411749b5beccde239e9cdadd1c19e6953 100644 GIT binary patch delta 20 acmdnTy^ovwG%qg~0}$-F@pL2iW>x?{JqCIJ delta 20 acmdnTy^ovwG%qg~0}w<`zOj*eGb;c(sRhLV From 95230df6ac63d551df46400b14c2727acd52bed5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:52:40 +0000 Subject: [PATCH 3/3] Address review feedback and clean unintended artifacts Agent-Logs-Url: https://github.com/dsaub/proyecto-final/sessions/e596d554-92ce-4f30-95b7-bcb209573c48 Co-authored-by: dsaub <54474838+dsaub@users.noreply.github.com> --- .../__pycache__/0001_initial.cpython-312.pyc | Bin 12957 -> 0 bytes ...ationcode_code_mode_and_more.cpython-312.pyc | Bin 1028 -> 0 bytes .../0003_order_transaction_code.cpython-312.pyc | Bin 2006 -> 0 bytes ...rvation_stockreservationitem.cpython-312.pyc | Bin 3069 -> 0 bytes .../0005_savedpaymentmethod.cpython-312.pyc | Bin 2338 -> 0 bytes .../0006_alter_category_name.cpython-312.pyc | Bin 768 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 174 -> 0 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 176 -> 0 bytes .../__pycache__/vat_filters.cpython-312.pyc | Bin 1470 -> 0 bytes tienda/tests.py | 2 ++ 10 files changed, 2 insertions(+) delete mode 100644 tienda/migrations/__pycache__/0001_initial.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/0002_verificationcode_code_mode_and_more.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/0003_order_transaction_code.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/0004_product_stock_stockreservation_stockreservationitem.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/0005_savedpaymentmethod.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc delete mode 100644 tienda/migrations/__pycache__/__init__.cpython-312.pyc delete mode 100644 tienda/templatetags/__pycache__/__init__.cpython-312.pyc delete mode 100644 tienda/templatetags/__pycache__/vat_filters.cpython-312.pyc diff --git a/tienda/migrations/__pycache__/0001_initial.cpython-312.pyc b/tienda/migrations/__pycache__/0001_initial.cpython-312.pyc deleted file mode 100644 index 9c5969213b1d88cb1648add5594aee554d5bb796..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12957 zcmcgzYjE3Eb|%4xNa|ryeoCSw=wZi_ElRR1MYiJ9dfJNhuw_Y(w3Zo&7o;GA1Py?4 z#B5$|y4^muO}p(z+ooN2yKR%2X`QC+GM&!EJN{8+`Xj(DXP`l*)1B$`hyH0BcRG_F zZO^?RB~l_IX=2wi;l;&0IQN|IJTIX9OKGXg1poeN?LW9PEhf{y<4NY9Eq(FDB3!&< zLMCM9O|xb)Qt8o>9<5}gcw5*$Yd2$B3ttp=%sRr(S*O`VnJ$@-^(_;!v5v(8>yr5*e8jB9d+o3y9^Vz!n3OsX9IR;-`nP zv@DTymO|!P3!*~iSu15?ZHpz@j~Lg;g78|=qC3x;8JUp{*%L+gt;TAQLJmS*o`>3* zaNRd&=t5MY_&#OK3hrI<#P%*l&O}*eKalbOkund~$er+PNr}n}YGX#F=pZ_jIJ~8M z$hrrej_gyXqXi{6hK?pGvUI8>lt=O)kL^^I@Mio7^sgqg$MVqHPz|b0&{?!~1tq9Q z4V)P@qT|TSmM(gCa$Bs)bC1q*uVLrC`{`RDGpb9RxNkS6Ap0`~__d+-9Q@7{q&1@sbT)^!lhEd|A>eyrr(}yK^W1CR zc@K3Zp3U-J1g47gl^Y;yTs3mV>sHnA=@Qh$iys2nLq1Y*=LlF6hahy zXB3$eV_9g96WTmB2D+k6x#a<yZSr+VPV~DuC(z$3Ncmm#`#F?

|A$ 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 Hov`~q2;Vjr diff --git a/tienda/migrations/__pycache__/0002_verificationcode_code_mode_and_more.cpython-312.pyc b/tienda/migrations/__pycache__/0002_verificationcode_code_mode_and_more.cpython-312.pyc deleted file mode 100644 index fa262e25d2885051d4447ec176dab4f47c77335f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1028 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 diff --git a/tienda/migrations/__pycache__/0005_savedpaymentmethod.cpython-312.pyc b/tienda/migrations/__pycache__/0005_savedpaymentmethod.cpython-312.pyc deleted file mode 100644 index 924cd09df61a59aafc2e71e3d504a36e7fb4206b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2338 zcmb7FOKcNI7~Wmm>qnf#&O3(04mAY3$^(c+OPfj|4@yfCL>>Y|yG%S2XOs1=nO%n( z^-v@ZJ#s6D9&!Z5s_3aX^nlb$q+VtvOSVR;TB+(GH=|Hh;!t&V*KR@sNZplo=KtrP zZ@&5Gn}2_9ZVqtl`LX(k{GG^gzgtG*@zgu}*I4Hp4swuJxHQjNK@oDUw2QZVF2$Ym zq&+z?E%KbeUE-ko0S7(Ev)8;Ea{$0`ZFOURMX*>Qn^9VTe3nnU1P-~^?HwJS!ZF6s8WUf~3A)+qfnv$K z?RI9Z3eaaqhh9aoEa-=UtN@!z!ELs_^|0vXH%7O>)>7zDWcZDdZrBFfOC602G1Sig z5!?0F*zQ+wd)^xRDu-S>wvlsZ!}gbDEK)jlXlKXm$c{rH^)7Um`nGw;V7uzKDT5I(5Ts3cEaw0&N*kz1{R~%p*q9odFn9?;^nK$N(1WJSgUYLPfUhRiNxySA@B}nKez$Ae~)0@P(ux zHEXOMpR{+0cgw=!~ylQyI9zAZZXI#Y(!9Q^C** z)}Cj7dSYl$i$N62gRBXU4al0*ZN#EN7=L98E?q&o$mShFk(qCAr- zQZq8ru~ph=)1P#vpzisfZd&LPJP z%xD3va zfR;z~BL9TjXCd6nTO(^sOCz|TstAwV)$qm$yIn<;F|?socGk*&P3Izx9Q-au*p*C4 zx^=tiI&)+;oG)T8Tf+YMSVzCl%FzR^CUX9HzUC4-#ILT@0-Qhkb)e$wH+}up@X6o8 zouoVYaI?~#G`o{K?%g;^OGL%xZk>dTYPg@oMz_RDY}Aa6k_)qBK11GzdP`RG(7k~Aa9rqh#RBR##=O|HWXeKYMo6TEwuCwQuH zCJ7(ybyPGASB9N`{%}J|c$|gNruukBQ&$~L`Tw0^YulEybIg+#t_?oTdM|w+Iv3<4 te3_Znk4U%v2{jkb^ZO#l5B$Q#e&tTT5RdWV!$2hzH$(A1IcC@x{{YZ5hAjX9 diff --git a/tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc b/tienda/migrations/__pycache__/0006_alter_category_name.cpython-312.pyc deleted file mode 100644 index 0306e68f36756469dc5334c725a410e5a8b82fca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 768 zcmZ8f&ubGw6n?Y2neHyF)F4FB3Wgx$U^W*$Ns9;yqCI(9kYSpcCL8xhI=fN#rqF{o zd+WcTw10pH{{X>LKuDOYc<3#ViqMlYyIG>n;qCX$y!Yn&-t4!H4Hwk(vW!#5BZ@EDkJ2+ZVYh^wO2_rxr*Z{}9JcBV<&kZu(812F?ssXTvq^1K(h%k9^YrZVd~;f0uxg!Sop(Uix=mGiI^cDLygE zlhd_Img;BccH5E8vr3VYhqHjkjLJs0+r3Lt`iip=%_AOX5zht*lXj10yq}0%S}~3I z*;m!~dEtw`At8_Z*+AlP92|^!+f;6~L!vW<)-7xM0T0=(y4EJeHX+g_M5T>GuJAe` z2V)xQ0?im7DLFju1w0jcp3;2~W?VedU4*4KoQDHia_J@`zMSnRSg1p|c;O9_hdlY6&#mwEo%1!zapxwk<9AQQ z)vDue+ma3&(zu^=*j{12FkIf*&}l{PHH9wh`{e|;#dSs1j|}Mz73L;F=r;z`KF8|_ RAGy=b)?~Bw3lu4;{{Z$P#BKlp diff --git a/tienda/migrations/__pycache__/__init__.cpython-312.pyc b/tienda/migrations/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index f910764f4854a0d0eeec43328d05b0480d1fc2d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmX@j%ge<81bc2g%>>bpK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%U3@mKQ~pss5CDx zwMf4_zbIS3peVmGHMt~TH!U+SF$bMnl9`&9lBl1XnO>Awl9``ZtREkrnU`4-AFo$X h`HRCQH$SB`C)KWq6=*sm5Ep|OADI~$8H<>KECBN(F1P>y diff --git a/tienda/templatetags/__pycache__/__init__.cpython-312.pyc b/tienda/templatetags/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 6a9d06aa8c65713800d5d07db1982561ad1b5c22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmX@j%ge<81bc2g%>>bpK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%U?euKQ~pss5CDx zwMf4_zbIS3peVmGHMt~TH!U+SF$bMnl9`&9lBi#jnp=>QSdvxoI}SE#z(LGl6e6rBIb=atmfaYG<`T;3n%J?sYpkv$ z&JZChg27XkJ?NqbVLfa%#|2OR2VN3#Ff#wR_SH1qe z?>+18P6CdnYp1xX{JLDK`If+`V7~fDg6iH}G=2cxPnZl!kBjdw^BSU4< zR*HdG$7f4oxCaT4JG-;6)WkQ5R5{U@B{q>=tyvvG9nY|}r6ySy;4sKoYhUirQqhip zFbK>-S-T7uU-^}Tb+rFPZ_%QzRTZr4@C{YLhB|uD2#t!% zsSFLlr+ize8p7l#b*I>n5L|?bky^C@7q!rsRvV#F6;7E2>Hy2y9xh$w9xI6{-)EvU z!^O2yRqz@sOMblK_|*M$DjnwAwA5lz(utrnP33sSaU~N$U$qv-scnzrn#kfE>c4|? z80KIz)@9_@dvXixuk^gJp4q!_@jiWg^lx`1ckJKaS2Lw$v-EB~z3*XSId$kQ5F@vi z*?Tv=sUzABlv{3DhvA>>fc}IG=pSjO{yfsmNAPW$wJE05BHyi+Rxb=Q$%Rj?fVPfK&7cwg0Ck$U?>*1x9T<@dntC`}m zS^Nr91q&W!PwD@YQyrN44R7hpl=Ae0uE@EqmWL0Fb#$&)KlC#f9<~=%JTx3@mAP;A zBOPVOqb>{TdS8E~sEY(vAW|wQZ9gX`U5Snwj|?kvnDYZZ!AT6C(zCXE|9yQSzibx1 zLd&i!{F&|5ACq4F*J3h^*^|_t;6iyPOf|gSH%&#*=%x5C&J{sLEi|zkod69U#u`SC zyd6mv6}AXlcmhPCZkuO3Zcn-FtSI1`YS(~bKpzwG0`f1wdYQ`4pMIXoucY#;sluH3 ik|gdN{NvyoEk<%%I%wHVg*JOYOMcsW^rbj}x4Z{mdN!p1 diff --git a/tienda/tests.py b/tienda/tests.py index 9fdccd5..1f6b083 100644 --- a/tienda/tests.py +++ b/tienda/tests.py @@ -1802,6 +1802,8 @@ class EndpointViewTests(TestCase): "confirm_password": "PasswordNueva123!", }) self.assertEqual(changed.status_code, 302) + self.buyer.refresh_from_db() + self.assertTrue(self.buyer.check_password("PasswordNueva123!")) invalid_city = self.client.post(reverse("crear_direccion"), data={ "full_name": "Comprador Uno",