From 661129d0a17f5ba2dacdc19a3d9fb687ad28cc2d Mon Sep 17 00:00:00 2001 From: Ali Asadi Date: Wed, 19 Aug 2026 14:34:15 +0330 Subject: [PATCH] Document product list query params for swagger; add filter test coverage Same gap as the stores endpoint: store/category/search filters on GET /api/v1/products/ already worked but weren't declared to drf-spectacular. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 2 ++ apps/catalog/tests/test_product_list.py | 37 +++++++++++++++++++++++++ apps/catalog/views.py | 10 +++++++ 3 files changed, 49 insertions(+) create mode 100644 apps/catalog/tests/test_product_list.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 6811619..29c04a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixed - `GET /api/v1/stores/` — documented the `category`, `neighborhood`, `search`, `lat`, and `lng` query params for drf-spectacular so they render in swagger. The filtering itself already worked; only the OpenAPI schema was missing them (`apps/stores/views.py`). +- `GET /api/v1/products/` — same issue: documented the existing `store`, `category`, and `search` query params for swagger (`apps/catalog/views.py`). ### Added - Test coverage for `StoreViewSet` list filtering (`apps/stores/tests/test_store_list.py`) — previously untested. +- Test coverage for `ProductViewSet` list filtering (`apps/catalog/tests/test_product_list.py`) — previously untested. diff --git a/apps/catalog/tests/test_product_list.py b/apps/catalog/tests/test_product_list.py new file mode 100644 index 0000000..ad0b110 --- /dev/null +++ b/apps/catalog/tests/test_product_list.py @@ -0,0 +1,37 @@ +from apps.catalog.models import ProductCategory + +from apps.orders.tests.base import OrdersTestCase + + +class ProductListFilterTests(OrdersTestCase): + def test_filter_by_store(self): + response = self.client.get('/api/v1/products/', {'store': str(self.store1.uuid)}) + + self.assertEqual(response.status_code, 200) + uuids = {product['uuid'] for product in response.data['results']} + self.assertEqual(uuids, {str(self.product1.uuid)}) + + def test_filter_by_category(self): + other_category = ProductCategory.objects.create(name='نوشیدنی') + self.product2.category = other_category + self.product2.save(update_fields=['category']) + + response = self.client.get('/api/v1/products/', {'category': str(self.product1.category.uuid)}) + + self.assertEqual(response.status_code, 200) + uuids = {product['uuid'] for product in response.data['results']} + self.assertEqual(uuids, {str(self.product1.uuid)}) + + def test_filter_by_search(self): + response = self.client.get('/api/v1/products/', {'search': 'کاله'}) + + self.assertEqual(response.status_code, 200) + uuids = {product['uuid'] for product in response.data['results']} + self.assertEqual(uuids, {str(self.product1.uuid)}) + + def test_no_filter_returns_all_products(self): + response = self.client.get('/api/v1/products/') + + self.assertEqual(response.status_code, 200) + uuids = {product['uuid'] for product in response.data['results']} + self.assertEqual(uuids, {str(self.product1.uuid), str(self.product2.uuid)}) diff --git a/apps/catalog/views.py b/apps/catalog/views.py index 339a660..6d62518 100644 --- a/apps/catalog/views.py +++ b/apps/catalog/views.py @@ -1,4 +1,5 @@ from django.db.models import Q +from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view from rest_framework import mixins, viewsets from rest_framework.decorators import action from rest_framework.permissions import AllowAny, IsAuthenticated @@ -26,6 +27,15 @@ class ProductCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): queryset = ProductCategory.objects.all() +@extend_schema_view( + list=extend_schema( + parameters=[ + OpenApiParameter('store', str, description='Filter by store UUID.'), + OpenApiParameter('category', str, description='Filter by product category UUID.'), + OpenApiParameter('search', str, description='Search by product name/description.'), + ], + ), +) class ProductViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """Customer-facing product browsing (store page, product detail, search)."""