Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions essenza/product/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.contrib.messages import get_messages # Para probar mensajes
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
Expand Down Expand Up @@ -439,3 +440,148 @@ def test_catalog_detail_returns_404_for_nonexistent_product(self):
url = reverse("catalog_detail", args=[9999])
response = self.client.get(url)
self.assertEqual(response.status_code, 404)


class StockTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(
username="user",
email="user@example.com",
password="pass1234",
role="user",
)
cls.admin = User.objects.create_user(
username="admin",
email="admin@example.com",
password="pass1234",
role="admin",
)

cls.product_high = Product.objects.create(
name="Producto Alto", stock=20, price=10
)
cls.product_low = Product.objects.create(
name="Producto Bajo", stock=5, price=10
)
cls.product_out = Product.objects.create(
name="Producto Agotado", stock=0, price=10
)

cls.stock_url = reverse("stock")
cls.login_url = reverse("login")
cls.dashboard_url = reverse("dashboard")

# --- TESTS DE ACCESO ---

def test_anonymous_user_redirects_to_dashboard(self):
resp = self.client.get(self.stock_url)
self.assertEqual(resp.status_code, 302)
self.assertRedirects(resp, self.dashboard_url)

def test_non_admin_user_redirects_to_dashboard(self):
self.client.login(email=self.user.email, password="pass1234")
resp = self.client.get(self.stock_url)
self.assertEqual(resp.status_code, 302)
self.assertRedirects(resp, self.dashboard_url)

def test_admin_user_succeeds_get(self):
self.client.login(email=self.admin.email, password="pass1234")
resp = self.client.get(self.stock_url)
self.assertEqual(resp.status_code, 200)
self.assertTemplateUsed(resp, "product/stock.html")

# --- TESTS DE FUNCIONALIDAD ---

def test_stock_page_shows_all_products(self):
self.client.login(email=self.admin.email, password="pass1234")
resp = self.client.get(self.stock_url)

# Comprobamos que aparecen los 3 productos
self.assertEqual(len(resp.context["products"]), 3)

# Comprobamos el HTML
self.assertContains(resp, "Producto Alto")
self.assertContains(
resp, '<span class="stock-ok">En Stock: 20</span>', html=True
)

self.assertContains(resp, "Producto Bajo")
self.assertContains(
resp, '<span class="stock-low">Stock Bajo: 5</span>', html=True
)

self.assertContains(resp, "Producto Agotado")
self.assertContains(
resp, '<span class="stock-out">Agotado (0)</span>', html=True
)

def test_post_admin_updates_stock_successfully(self):
"""5. CORRECTO: Un admin puede actualizar el stock (Test 7 en tu código)."""
self.client.login(email=self.admin.email, password="pass1234")

self.assertEqual(self.product_high.stock, 20) # Stock inicial

data = {"product_id": self.product_high.pk, "stock": 15}
resp = self.client.post(
self.stock_url, data, follow=True
) # Se hace una petición POST para actualizar el stock a 15

# Comprobamos que volvemos a la página de stock
self.assertEqual(resp.status_code, 200)
self.assertTemplateUsed(resp, "product/stock.html")

# Comprobamos que la base de datos se actualizó correctamente
self.product_high.refresh_from_db()
self.assertEqual(self.product_high.stock, 15)

# Comprobamos el mensaje de éxito
messages = list(get_messages(resp.context["request"]))
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), "Stock de 'Producto Alto' actualizado a 15.")

def test_post_admin_invalid_product_returns_404(self):
self.client.login(email=self.admin.email, password="pass1234")
data = {"product_id": 999, "stock": 15} # ID 999 no existe

resp = self.client.post(self.stock_url, data)
self.assertEqual(resp.status_code, 404)

def test_post_admin_invalid_stock_value_shows_error(self):
self.client.login(email=self.admin.email, password="pass1234")

# Enviamos un valor de stock no numérico
data = {"product_id": self.product_high.pk, "stock": "abc"}
resp = self.client.post(self.stock_url, data, follow=True)

# Comprobamos que volvemos a la página de stock
self.assertEqual(resp.status_code, 200)

# Comprobamos que el stock NO se actualizó
self.product_high.refresh_from_db()
self.assertEqual(self.product_high.stock, 20)

# Comprobamos el mensaje de error
messages = list(get_messages(resp.context["request"]))
self.assertEqual(len(messages), 1)
self.assertEqual(
str(messages[0]), "El valor de stock 'abc' no es un número válido."
)

def test_post_admin_negative_stock_value_shows_error(self):
self.client.login(email=self.admin.email, password="pass1234")

# Enviamos un valor de stock negativo y comprobamos el error
data = {"product_id": self.product_high.pk, "stock": "-5"}
resp = self.client.post(self.stock_url, data, follow=True)

self.assertEqual(resp.status_code, 200)

self.product_high.refresh_from_db()
self.assertEqual(self.product_high.stock, 20) # No cambia

messages = list(get_messages(resp.context["request"]))
self.assertEqual(len(messages), 1)
self.assertEqual(
str(messages[0]), "El valor de stock '-5' no es un número válido."
)
41 changes: 25 additions & 16 deletions essenza/product/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ class StockView(LoginRequiredMixin, UserPassesTestMixin, View):
def test_func(self):
return self.request.user.is_authenticated and self.request.user.role == "admin"

# Solo se ejecutan métodos GET y POST si el usuario pasa la prueba
# Redirige a 'dashboard' si no pasa el test_func
def handle_no_permission(self):
return redirect("dashboard")

def get(self, request):
# Carga y muestra todos los productos ordenados por nombre
products = Product.objects.all().order_by("name")
Expand All @@ -62,21 +65,27 @@ def get(self, request):
def post(self, request):
# Coge datos del formulario para actualizar stock
product_id = request.POST.get("product_id")
stock = request.POST.get("stock")

# Encuentra el producto por su ID
product = Product.objects.get(pk=product_id)
if not product:
messages.error(request, "Producto no encontrado.")
return redirect("stock")

# Actualiza el stock del producto
new_stock = int(stock or 0)
product.stock = new_stock
product.save(update_fields=["stock"])
messages.success(
request, f"Stock de '{product.name}' actualizado a {new_stock}."
)
stock_value = request.POST.get("stock") # Renombrado para claridad

product = get_object_or_404(Product, pk=product_id)

try:
# Comprobamos si el valor es un número
new_stock = int(stock_value or 0)
if new_stock < 0:
# No permitir stock negativo
raise ValueError("El stock no puede ser negativo")

product.stock = new_stock
product.save(update_fields=["stock"])
messages.success(
request, f"Stock de '{product.name}' actualizado a {new_stock}."
)

except (ValueError, TypeError):
messages.error(
request, f"El valor de stock '{stock_value}' no es un número válido."
)

# Recarga la misma página
return redirect("stock")
Expand Down