feat: add product activation/deactivation and filtering by active status
- Add 'active' boolean field to Product model with default=True - Implement ProductView.get_queryset() to filter products by active status - Default behavior: return only active products - Support query params: ?active=true|false|all - Support variations: 1/0, yes/no for true/false - Detail operations (GET/PATCH/DELETE by ID) work with all products - Update ProductSerializer to include 'active' field - Add comprehensive test suite (11 new tests): - Test filtering by active/inactive/all products - Test parameter variations (1, yes, 0, no) - Test PATCH to activate/deactivate products - Test default list behavior after status changes - Update API documentation in doc/requests.org with examples - All tests passing (13 product tests + 8 API tests)
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
from django.test import Client, TestCase
|
||||
from django.conf import settings
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
from ..models.products import ProductCategory, Product
|
||||
from .Mixins import LoginMixin
|
||||
|
||||
import os
|
||||
import json
|
||||
@@ -36,6 +39,211 @@ class TestProducts(TestCase):
|
||||
app_dir = os.path.join(settings.BASE_DIR, app_name)
|
||||
example_csv = os.path.join(app_dir, csv_file)
|
||||
with open(example_csv, "rb") as csv:
|
||||
self.client.post(
|
||||
"/don_confiao/importar_productos", {"csv_file": csv}
|
||||
)
|
||||
self.client.post("/don_confiao/importar_productos", {"csv_file": csv})
|
||||
|
||||
|
||||
class TestProductsAPIFiltering(APITestCase, LoginMixin):
|
||||
"""Tests for filtering products by active status via API"""
|
||||
|
||||
def setUp(self):
|
||||
self.login()
|
||||
|
||||
# Create active products
|
||||
self.active_product_1 = Product.objects.create(
|
||||
name="Active Product 1", price=100.00, active=True
|
||||
)
|
||||
self.active_product_2 = Product.objects.create(
|
||||
name="Active Product 2", price=200.00, active=True
|
||||
)
|
||||
|
||||
# Create inactive products
|
||||
self.inactive_product_1 = Product.objects.create(
|
||||
name="Inactive Product 1", price=150.00, active=False
|
||||
)
|
||||
self.inactive_product_2 = Product.objects.create(
|
||||
name="Inactive Product 2", price=250.00, active=False
|
||||
)
|
||||
|
||||
def test_get_products_default_returns_only_active(self):
|
||||
"""By default, API should return only active products"""
|
||||
response = self.client.get("/don_confiao/api/products/")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = response.json()
|
||||
self.assertEqual(len(data), 2)
|
||||
|
||||
product_names = [p["name"] for p in data]
|
||||
self.assertIn("Active Product 1", product_names)
|
||||
self.assertIn("Active Product 2", product_names)
|
||||
self.assertNotIn("Inactive Product 1", product_names)
|
||||
self.assertNotIn("Inactive Product 2", product_names)
|
||||
|
||||
def test_get_products_active_true(self):
|
||||
"""Filter products with active=true should return only active products"""
|
||||
response = self.client.get("/don_confiao/api/products/?active=true")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = response.json()
|
||||
self.assertEqual(len(data), 2)
|
||||
|
||||
for product in data:
|
||||
self.assertTrue(product["active"])
|
||||
|
||||
def test_get_products_active_false(self):
|
||||
"""Filter products with active=false should return only inactive products"""
|
||||
response = self.client.get("/don_confiao/api/products/?active=false")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = response.json()
|
||||
self.assertEqual(len(data), 2)
|
||||
|
||||
for product in data:
|
||||
self.assertFalse(product["active"])
|
||||
|
||||
product_names = [p["name"] for p in data]
|
||||
self.assertIn("Inactive Product 1", product_names)
|
||||
self.assertIn("Inactive Product 2", product_names)
|
||||
|
||||
def test_get_products_active_all(self):
|
||||
"""Filter products with active=all should return all products"""
|
||||
response = self.client.get("/don_confiao/api/products/?active=all")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = response.json()
|
||||
self.assertEqual(len(data), 4)
|
||||
|
||||
def test_get_products_active_variations(self):
|
||||
"""Test different variations of true/false values"""
|
||||
# Test '1' for true
|
||||
response = self.client.get("/don_confiao/api/products/?active=1")
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
|
||||
# Test 'yes' for true
|
||||
response = self.client.get("/don_confiao/api/products/?active=yes")
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
|
||||
# Test '0' for false
|
||||
response = self.client.get("/don_confiao/api/products/?active=0")
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
|
||||
# Test 'no' for false
|
||||
response = self.client.get("/don_confiao/api/products/?active=no")
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
|
||||
def test_get_product_detail_regardless_of_status(self):
|
||||
"""Getting a specific product by ID should work regardless of active status"""
|
||||
# Get active product
|
||||
response = self.client.get(
|
||||
f"/don_confiao/api/products/{self.active_product_1.id}/"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["name"], "Active Product 1")
|
||||
|
||||
# Get inactive product
|
||||
response = self.client.get(
|
||||
f"/don_confiao/api/products/{self.inactive_product_1.id}/"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["name"], "Inactive Product 1")
|
||||
|
||||
|
||||
class TestProductsAPIActivation(APITestCase, LoginMixin):
|
||||
"""Tests for activating/deactivating products via API"""
|
||||
|
||||
def setUp(self):
|
||||
self.login()
|
||||
|
||||
self.product = Product.objects.create(
|
||||
name="Test Product", price=100.00, active=True
|
||||
)
|
||||
|
||||
def test_deactivate_product_via_patch(self):
|
||||
"""PATCH request should be able to deactivate a product"""
|
||||
response = self.client.patch(
|
||||
f"/don_confiao/api/products/{self.product.id}/",
|
||||
{"active": False},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Verify product was deactivated
|
||||
self.product.refresh_from_db()
|
||||
self.assertFalse(self.product.active)
|
||||
|
||||
# Verify response contains updated data
|
||||
self.assertFalse(response.json()["active"])
|
||||
|
||||
def test_activate_product_via_patch(self):
|
||||
"""PATCH request should be able to activate a product"""
|
||||
# First deactivate the product
|
||||
self.product.active = False
|
||||
self.product.save()
|
||||
|
||||
response = self.client.patch(
|
||||
f"/don_confiao/api/products/{self.product.id}/",
|
||||
{"active": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Verify product was activated
|
||||
self.product.refresh_from_db()
|
||||
self.assertTrue(self.product.active)
|
||||
|
||||
# Verify response contains updated data
|
||||
self.assertTrue(response.json()["active"])
|
||||
|
||||
def test_update_other_fields_preserves_active_status(self):
|
||||
"""Updating other fields should not affect active status"""
|
||||
response = self.client.patch(
|
||||
f"/don_confiao/api/products/{self.product.id}/",
|
||||
{"price": "250.00"},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Verify active status was preserved
|
||||
self.product.refresh_from_db()
|
||||
self.assertTrue(self.product.active)
|
||||
self.assertEqual(self.product.price, 250.00)
|
||||
|
||||
def test_deactivated_product_not_in_default_list(self):
|
||||
"""After deactivating a product, it should not appear in default list"""
|
||||
# Deactivate product
|
||||
self.client.patch(
|
||||
f"/don_confiao/api/products/{self.product.id}/",
|
||||
{"active": False},
|
||||
format="json",
|
||||
)
|
||||
|
||||
# Get default product list
|
||||
response = self.client.get("/don_confiao/api/products/")
|
||||
data = response.json()
|
||||
|
||||
# Product should not be in list
|
||||
product_ids = [p["id"] for p in data]
|
||||
self.assertNotIn(self.product.id, product_ids)
|
||||
|
||||
def test_activated_product_appears_in_default_list(self):
|
||||
"""After activating a product, it should appear in default list"""
|
||||
# Deactivate product first
|
||||
self.product.active = False
|
||||
self.product.save()
|
||||
|
||||
# Activate product
|
||||
self.client.patch(
|
||||
f"/don_confiao/api/products/{self.product.id}/",
|
||||
{"active": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
# Get default product list
|
||||
response = self.client.get("/don_confiao/api/products/")
|
||||
data = response.json()
|
||||
|
||||
# Product should be in list
|
||||
product_ids = [p["id"] for p in data]
|
||||
self.assertIn(self.product.id, product_ids)
|
||||
|
||||
Reference in New Issue
Block a user