diff --git a/config/api/__init__.py b/config/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/config/api/v1/__init__.py b/config/api/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/config/api/v1/mixins.py b/config/api/v1/mixins.py deleted file mode 100644 index 533c4b3..0000000 --- a/config/api/v1/mixins.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import Dict - - -class CRUDPermissionsMixin: - view_permission_codes: tuple = () - create_permission_codes: tuple = () - destroy_permission_codes: tuple = () - update_permission_codes: tuple = () - action_permissions: Dict[str, tuple] = {} - - def get_default_action_permissions(self): - return { - "list": self.view_permission_codes, - "retrieve": self.view_permission_codes, - "create": self.create_permission_codes, - "update": self.update_permission_codes, - "partial_update": self.update_permission_codes, - "destroy": self.destroy_permission_codes, - } - - def has_perm_for_action(self, request, action) -> bool: - default_action_permissions = self.get_default_action_permissions() - action_permissions = {**default_action_permissions, **self.action_permissions} - perms = action_permissions.get(action, ()) - return request.user.has_perms(perms) - - def has_permission(self, request, view): - user = request.user - - if user.is_superuser: - return True - - if not user.is_staff: - return False - - return self.has_perm_for_action(request=request, action=view.action) diff --git a/config/api/v1/permissions.py b/config/api/v1/permissions.py deleted file mode 100644 index e69de29..0000000 diff --git a/config/api/v1/urls.py b/config/api/v1/urls.py deleted file mode 100644 index 2588df2..0000000 --- a/config/api/v1/urls.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.urls import include, path -from drf_spectacular.views import SpectacularRedocView, SpectacularSwaggerView - -from config.api.v1.views import APISchema - -urlpatterns = [ - path("schema/", APISchema.as_view(), name="schema"), - path( - "swagger/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui" - ), - path("redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), - path("shop/", include("shop.api.v1.routers", namespace="shop_api")), - path("files/", include("files.api.v1.urls", namespace="files")), - path("auth/", include("users.api.v1.urls", namespace="auth")), -] diff --git a/config/api/v1/views.py b/config/api/v1/views.py deleted file mode 100644 index 0ca967f..0000000 --- a/config/api/v1/views.py +++ /dev/null @@ -1,5 +0,0 @@ -from drf_spectacular.views import SpectacularAPIView - - -class APISchema(SpectacularAPIView): - api_version = "v1" diff --git a/config/urls.py b/config/urls.py index 1e1dc25..e661890 100644 --- a/config/urls.py +++ b/config/urls.py @@ -7,7 +7,6 @@ from django.urls import include, path urlpatterns = [ path("", include("web.urls", namespace="web")), path("admin/", admin.site.urls), - path("api/v1/", include("config.api.v1.urls")), path("watchman/", include("watchman.urls")), path("auth/", include("users.urls", namespace="users")), path("tpv/", include("shop.urls", namespace="shop")), diff --git a/shop/api/__init__.py b/shop/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/shop/api/v1/__init__.py b/shop/api/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/shop/api/v1/permissions.py b/shop/api/v1/permissions.py deleted file mode 100644 index ab9e0fc..0000000 --- a/shop/api/v1/permissions.py +++ /dev/null @@ -1,17 +0,0 @@ -from rest_framework.permissions import BasePermission - -from config.api.v1.mixins import CRUDPermissionsMixin - - -class ProductPermissions(CRUDPermissionsMixin, BasePermission): - view_permission_codes = ("shop.view_product",) - create_permission_codes = ("shop.add_product",) - destroy_permission_codes = ("shop.delete_product",) - update_permission_codes = ("shop.change_product",) - - -class ProductPricePermissions(CRUDPermissionsMixin, BasePermission): - view_permission_codes = ("shop.view_productprice",) - create_permission_codes = ("shop.add_productprice",) - destroy_permission_codes = ("shop.delete_productprice",) - update_permission_codes = ("shop.change_productprice",) diff --git a/shop/api/v1/routers.py b/shop/api/v1/routers.py deleted file mode 100644 index 55ad122..0000000 --- a/shop/api/v1/routers.py +++ /dev/null @@ -1,31 +0,0 @@ -from rest_framework.routers import DefaultRouter - -from shop.api.v1.viewsets import ( - BrandViewSet, - OrderLineViewSet, - OrderViewSet, - ProductBatchViewSet, - ProductPriceViewSet, - ProductViewSet, - ProviderViewSet, - TagViewSet, - TaxViewSet, -) - -app_name = "shop" - - -router = DefaultRouter() - -router.register("taxes", TaxViewSet) -router.register("tags", TagViewSet) -router.register("brands", BrandViewSet) -router.register("providers", ProviderViewSet) -router.register("products", ProductViewSet) -router.register("products/(?P[^/.]+)/prices", ProductPriceViewSet) -router.register("product-batches", ProductBatchViewSet) -router.register("orders", OrderViewSet) -router.register("orders/(?P[^/.]+)/lines", OrderLineViewSet) - - -urlpatterns = router.urls diff --git a/shop/api/v1/serializers.py b/shop/api/v1/serializers.py deleted file mode 100644 index db6f95e..0000000 --- a/shop/api/v1/serializers.py +++ /dev/null @@ -1,252 +0,0 @@ -from django.utils import timezone -from rest_framework import serializers - -from files.api.v1.serializers import FileUploadSerializer, NoIDFileUploadSerializer -from shop.models import ( - Brand, - Order, - OrderLine, - Product, - ProductBatch, - ProductPrice, - Provider, - Tag, - Tax, -) - - -class TaxSerializer(serializers.ModelSerializer): - class Meta: - model = Tax - fields = ( - "id", - "code", - "value", - ) - - -class TagSerializer(serializers.ModelSerializer): - class Meta: - model = Tag - fields = ( - "id", - "name", - ) - - -class ProductPriceSerializer(serializers.ModelSerializer): - tax = TaxSerializer() - - class Meta: - model = ProductPrice - fields = ( - "price", - "current", - "date", - "tax", - "price_with_tax", - ) - - -class CreateProductPriceSerializer(serializers.ModelSerializer): - class Meta: - model = ProductPrice - fields = ( - "id", - "price", - "tax", - ) - - -class ProductSerializer(serializers.ModelSerializer): - images = FileUploadSerializer(many=True, read_only=True) - price = ProductPriceSerializer() - - class Meta: - model = Product - fields = ( - "id", - "sku", - "name", - "description", - "stock", - "is_digital_asset", - "images", - "price", - "tags", - ) - - -class ListProductSerializer(serializers.ModelSerializer): - price = ProductPriceSerializer() - tags = TagSerializer(many=True) - images = NoIDFileUploadSerializer(many=True, read_only=True) - - class Meta: - model = Product - fields = ( - "id", - "sku", - "name", - "description", - "stock", - "is_digital_asset", - "price", - "tags", - "images", - ) - - -class CreateProductSerializer(serializers.ModelSerializer): - price = serializers.DecimalField( - max_digits=13, decimal_places=4, required=False, write_only=True - ) - tax = serializers.PrimaryKeyRelatedField( - queryset=Tax.objects.all(), write_only=True - ) - - def create(self, validated_data): - instance = Product.objects.create( - sku=validated_data.get("sku"), - name=validated_data.get("name"), - description=validated_data.get("description"), - stock=validated_data.get("stock"), - is_digital_asset=validated_data.get("is_digital_asset"), - url=validated_data.get("url"), - ) - - tags = validated_data.get("tags") - for tag in tags: - instance.tags.add(tag) - - ProductPrice.objects.create( - product=instance, - date=timezone.now(), - price=validated_data.get("price"), - tax=validated_data.get("tax"), - current=True, - ) - - return instance - - class Meta: - model = Product - fields = ( - "id", - "sku", - "name", - "description", - "stock", - "is_digital_asset", - "url", - "price", - "tax", - "tags", - "images", - ) - - -class UpdateProductSerializer(serializers.ModelSerializer): - class Meta: - model = Product - fields = ( - "id", - "sku", - "name", - "description", - "stock", - "is_digital_asset", - "url", - "tags", - "images", - ) - - -class ProductBatchSerializer(serializers.ModelSerializer): - class Meta: - model = ProductBatch - fields = ( - "id", - "code", - "product", - "quantity", - "creation_date", - "expiration_date", - ) - - -class ProviderSerializer(serializers.ModelSerializer): - class Meta: - model = Provider - fields = ( - "id", - "vat_id", - "name", - "email", - "phone", - "address", - "city", - "state", - "country", - "zip", - ) - - -class BrandSerializer(serializers.ModelSerializer): - class Meta: - model = Brand - fields = ( - "id", - "name", - ) - - -class OrderSerializer(serializers.ModelSerializer): - class Meta: - model = Order - fields = ( - "uuid", - "creation_date", - "last_modification_date", - "base_total", - "total", - "user", - "billing_address", - "billing_city", - "billing_state", - "billing_country", - "billing_zip", - "shipping_address", - "shipping_city", - "shipping_state", - "shipping_country", - "shipping_zip", - "contact_phone", - ) - read_only_fields = ( - "uuid", - "base_total", - "total", - ) - - -class OrderLineSerializer(serializers.ModelSerializer): - class Meta: - model = OrderLine - fields = ( - "id", - "product", - "quantity", - "price", - "base_total", - "total", - "taxes", - "tax_value", - ) - read_only_fields = ( - "price", - "base_total", - "total", - "taxes", - "tax_value", - ) diff --git a/shop/api/v1/viewsets.py b/shop/api/v1/viewsets.py deleted file mode 100644 index f43e734..0000000 --- a/shop/api/v1/viewsets.py +++ /dev/null @@ -1,194 +0,0 @@ -from decimal import Decimal - -from django.db import transaction -from django.db.models.deletion import ProtectedError -from django.shortcuts import get_object_or_404 -from django.utils.text import gettext_lazy as _ -from rest_framework.exceptions import ValidationError -from rest_framework.permissions import IsAdminUser -from rest_framework.viewsets import ModelViewSet - -from shop.api.v1.permissions import ProductPermissions, ProductPricePermissions -from shop.api.v1.serializers import ( - BrandSerializer, - CreateProductPriceSerializer, - CreateProductSerializer, - ListProductSerializer, - OrderLineSerializer, - OrderSerializer, - ProductBatchSerializer, - ProductPriceSerializer, - ProductSerializer, - ProviderSerializer, - TagSerializer, - TaxSerializer, - UpdateProductSerializer, -) -from shop.filters import ProductFilter -from shop.models import ( - Brand, - Order, - OrderLine, - Product, - ProductBatch, - ProductPrice, - Provider, - Tag, - Tax, -) -from shop.utils import delete_product_batch - - -class ProductViewSet(ModelViewSet): - serializer_class = ProductSerializer - queryset = ( - Product.objects.prefetch_related("prices") - .prefetch_related("tags") - .prefetch_related("images") - .all() - ) - permission_classes = ( - IsAdminUser, - ProductPermissions, - ) - search_fields = ("name",) - filterset_class = ProductFilter - - def get_serializer_class(self): - if self.action == "create": - return CreateProductSerializer - elif self.action == "list": - return ListProductSerializer - elif self.action in ("update", "partial_update"): - return UpdateProductSerializer - return self.serializer_class - - def perform_destroy(self, instance): - try: - super().perform_destroy(instance) - except ProtectedError: - raise ValidationError(_("No se puede borrar el producto")) - - -class ProductPriceViewSet(ModelViewSet): - serializer_class = ProductPriceSerializer - queryset = ProductPrice.objects.all() - permission_classes = ( - IsAdminUser, - ProductPricePermissions, - ) - - def get_queryset(self): - return super().get_queryset().filter(product_id=self.kwargs.get("product_id")) - - def get_serializer_class(self): - if self.action == "create": - return CreateProductPriceSerializer - return self.serializer_class - - def perform_create(self, serializer): - product = get_object_or_404(Product, pk=self.kwargs.get("product_id")) - product.prices.all().update(current=False) - serializer.save(product=product, current=True) - - -class ProductBatchViewSet(ModelViewSet): - serializer_class = ProductBatchSerializer - queryset = ProductBatch.objects.all() - permission_classes = (IsAdminUser,) - - def perform_create(self, serializer): - instance = serializer.save() - product = instance.product - product.stock += instance.quantity - product.save() - - def perform_destroy(self, instance): - delete_product_batch(instance) - - def perform_update(self, serializer): - instance = self.get_object() - product = instance.product - product.stock -= instance.quantity - - updated_batch = serializer.save() - product.stock += updated_batch.quantity - product.save() - - -class ProviderViewSet(ModelViewSet): - serializer_class = ProviderSerializer - queryset = Provider.objects.all() - permission_classes = (IsAdminUser,) - - -class TaxViewSet(ModelViewSet): - serializer_class = TaxSerializer - queryset = Tax.objects.all() - permission_classes = (IsAdminUser,) - search_fields = ("code",) - - -class OrderViewSet(ModelViewSet): - serializer_class = OrderSerializer - queryset = Order.objects.all() - lookup_field = "uuid" - - -class OrderLineViewSet(ModelViewSet): - serializer_class = OrderLineSerializer - queryset = OrderLine.objects.all() - - def perform_create(self, serializer): - with transaction.atomic(): - order = get_object_or_404(Order, uuid=self.kwargs.get("order_id")) - product = serializer.validated_data.get("product") - price = product.prices.last() - tax = price.tax.value - quantity = serializer.validated_data.get("quantity") - base_total = quantity * price.price - taxes = base_total * (tax / Decimal("100")) - total = base_total + taxes - - instance = serializer.save( - order=order, - price=price.price, - base_total=base_total, - tax_value=tax, - taxes=taxes, - total=total, - ) - - order.base_total += instance.base_total - order.total += instance.total - order.save() - - def perform_destroy(self, instance): - order = instance.order - - with transaction.atomic(): - order.base_total -= instance.base_total - order.total -= instance.total - instance.delete() - order.save() - - def get_queryset(self): - return ( - super() - .get_queryset() - .select_related("order") - .filter(order__uuid=self.kwargs.get("order_id")) - ) - - -class TagViewSet(ModelViewSet): - serializer_class = TagSerializer - queryset = Tag.objects.all() - permission_classes = (IsAdminUser,) - - -class BrandViewSet(ModelViewSet): - serializer_class = BrandSerializer - queryset = Brand.objects.all() - permission_classes = (IsAdminUser,) - search_fields = ("name",) diff --git a/shop/tests/api/__init__.py b/shop/tests/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/shop/tests/api/test_api_brands.py b/shop/tests/api/test_api_brands.py deleted file mode 100644 index 965143d..0000000 --- a/shop/tests/api/test_api_brands.py +++ /dev/null @@ -1,50 +0,0 @@ -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Brand - - -class TestBrandsAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "brand" - - def setUp(self): - self.create_user() - - def create_brands(self): - Brand.objects.create( - name="Marca", - ) - - def test_fetch_brands(self): - self.create_brands() - self.login() - response = self.client.get("/api/v1/shop/brands/") - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_retrieve_brand(self): - self.login() - response = self.client.post("/api/v1/shop/brands/", {"name": "Marca 1"}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/brands/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_brand(self): - self.login() - response = self.client.post("/api/v1/shop/brands/", {"name": "Marca 1"}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/brands/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch(f"/api/v1/shop/brands/{pk}/", {"name": "Marca 2"}) - assert response.status_code == status.HTTP_200_OK - - response = self.client.get(f"/api/v1/shop/brands/{pk}/") - assert response.status_code == status.HTTP_200_OK - - assert response.data.get("name") == "Marca 2" diff --git a/shop/tests/api/test_api_orders.py b/shop/tests/api/test_api_orders.py deleted file mode 100644 index a76a099..0000000 --- a/shop/tests/api/test_api_orders.py +++ /dev/null @@ -1,206 +0,0 @@ -from decimal import Decimal - -from django.contrib.auth import get_user_model -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import CustomerAddress, Product, ProductPrice, Tax - -User = get_user_model() - - -class TestOrdersAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "productprice" - - def setUp(self): - self.create_user() - self.tax = Tax.objects.create( - code="IVA", - value=21, - ) - self.customer = get_user_model().objects.create_user( - username="11111111H", - first_name="Darth", - last_name="Maull", - email="darth@maul.com", - password="dathomir", - ) - - self.customer_shipping_address = CustomerAddress.objects.create( - user=self.customer, - address="Dathomir", - address_town="Dathomir", - address_zip="00001", - address_state="Dathomir", - address_phone="900000000", - address_type=CustomerAddress.Types.SHIPPING, - ) - - self.customer_billing_address = CustomerAddress.objects.create( - user=self.customer, - address="Dathomir", - address_town="Dathomir", - address_zip="00001", - address_state="Dathomir", - address_phone="900000000", - address_type=CustomerAddress.Types.BILLING, - ) - self.create_products() - - def create_products(self): - self.product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - - ProductPrice.objects.create( - product=self.product, - price=Decimal("1.20"), - tax=self.tax, - ) - - def test_create_retrieve_order(self): - self.login() - - response = self.client.post( - f"/api/v1/shop/orders/", - { - "user": self.user.pk, - "billing_address": self.customer_billing_address.address, - "billing_city": self.customer_billing_address.address_town, - "billing_state": self.customer_billing_address.address_state, - "billing_country": self.customer_billing_address.address_country, - "billing_zip": self.customer_billing_address.address_zip, - "shipping_address": self.customer_shipping_address.address, - "shipping_city": self.customer_shipping_address.address_town, - "shipping_state": self.customer_shipping_address.address_state, - "shipping_country": self.customer_shipping_address.address_country, - "shipping_zip": self.customer_shipping_address.address_zip, - "contact_phone": self.customer_shipping_address.address_phone, - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("uuid") - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_retrieve_order_with_lines(self): - self.login() - - response = self.client.post( - f"/api/v1/shop/orders/", - { - "user": self.user.pk, - "billing_address": self.customer_billing_address.address, - "billing_city": self.customer_billing_address.address_town, - "billing_state": self.customer_billing_address.address_state, - "billing_country": self.customer_billing_address.address_country, - "billing_zip": self.customer_billing_address.address_zip, - "shipping_address": self.customer_shipping_address.address, - "shipping_city": self.customer_shipping_address.address_town, - "shipping_state": self.customer_shipping_address.address_state, - "shipping_country": self.customer_shipping_address.address_country, - "shipping_zip": self.customer_shipping_address.address_zip, - "contact_phone": self.customer_shipping_address.address_phone, - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("uuid") - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - assert Decimal(response.data.get("base_total")) == Decimal("0.00") - assert Decimal(response.data.get("total")) == Decimal("0.00") - - response = self.client.post( - f"/api/v1/shop/orders/{pk}/lines/", - { - "product": self.product.pk, - "quantity": "5", - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - response = self.client.get(f"/api/v1/shop/orders/{pk}/lines/") - assert len(response.data.get("results")) == 1 - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - - assert Decimal(response.data.get("base_total")) == Decimal("6.00") - assert Decimal(response.data.get("total")) == Decimal("6.00") + ( - Decimal("6.00") * Decimal("0.21") - ) - - def test_create_delete_order_with_lines(self): - self.login() - - # Create order - response = self.client.post( - f"/api/v1/shop/orders/", - { - "user": self.user.pk, - "billing_address": self.customer_billing_address.address, - "billing_city": self.customer_billing_address.address_town, - "billing_state": self.customer_billing_address.address_state, - "billing_country": self.customer_billing_address.address_country, - "billing_zip": self.customer_billing_address.address_zip, - "shipping_address": self.customer_shipping_address.address, - "shipping_city": self.customer_shipping_address.address_town, - "shipping_state": self.customer_shipping_address.address_state, - "shipping_country": self.customer_shipping_address.address_country, - "shipping_zip": self.customer_shipping_address.address_zip, - "contact_phone": self.customer_shipping_address.address_phone, - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("uuid") - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - - # Assert order is created with 0.00€ - assert Decimal(response.data.get("base_total")) == Decimal("0.00") - assert Decimal(response.data.get("total")) == Decimal("0.00") - - # Create order line - response = self.client.post( - f"/api/v1/shop/orders/{pk}/lines/", - { - "product": self.product.pk, - "quantity": "5", - }, - ) - - line_pk = response.data.get("id") - - assert response.status_code == status.HTTP_201_CREATED - response = self.client.get(f"/api/v1/shop/orders/{pk}/lines/") - assert len(response.data.get("results")) == 1 - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - - # Assert order totals are set - assert Decimal(response.data.get("base_total")) == Decimal("6.00") - assert Decimal(response.data.get("total")) == Decimal("6.00") + ( - Decimal("6.00") * Decimal("0.21") - ) - - # Delete line - response = self.client.delete(f"/api/v1/shop/orders/{pk}/lines/{line_pk}/") - assert response.status_code == status.HTTP_204_NO_CONTENT - - response = self.client.get(f"/api/v1/shop/orders/{pk}/") - assert response.status_code == status.HTTP_200_OK - - # Assert order totals are 0.00 - assert Decimal(response.data.get("base_total")) == Decimal("0.00") - assert Decimal(response.data.get("total")) == Decimal("0.00") diff --git a/shop/tests/api/test_api_product_batches.py b/shop/tests/api/test_api_product_batches.py deleted file mode 100644 index e67c439..0000000 --- a/shop/tests/api/test_api_product_batches.py +++ /dev/null @@ -1,120 +0,0 @@ -from decimal import Decimal - -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Product, Provider - - -class TestProductBatchesAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "productbatch" - - def setUp(self): - self.create_user() - - def create_provider(self): - return Provider.objects.create( - vat_id="11111111H", - name="Mandalorians", - email="din@djarin.com", - phone="612345678", - address="Mandalore", - city="Mandalore", - state="Mandalore", - country="Mandalore", - zip="12345", - ) - - def create_product(self): - self.product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - - def test_create_retrieve_product_batch(self): - self.login() - self.create_product() - provider = self.create_provider() - response = self.client.post( - "/api/v1/shop/product-batches/", - { - "code": "B00001", - "product": self.product.pk, - "quantity": "5", - "provider": provider.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/product-batches/{pk}/") - self.product.refresh_from_db() - assert self.product.stock == Decimal("5") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_product_batch(self): - self.login() - self.create_product() - provider = self.create_provider() - - response = self.client.post( - "/api/v1/shop/product-batches/", - { - "code": "B00001", - "product": self.product.pk, - "quantity": "5", - "provider": provider.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/product-batches/{pk}/") - assert response.status_code == status.HTTP_200_OK - - self.product.refresh_from_db() - assert self.product.stock == Decimal("5") - - response = self.client.patch( - f"/api/v1/shop/product-batches/{pk}/", - { - "quantity": "10", - }, - ) - assert response.status_code == status.HTTP_200_OK - - self.product.refresh_from_db() - assert self.product.stock == Decimal("10") - - response = self.client.get(f"/api/v1/shop/product-batches/{pk}/") - assert response.status_code == status.HTTP_200_OK - assert Decimal(response.data.get("quantity")) == Decimal("10") - - def test_create_delete_product_batch(self): - self.login() - self.create_product() - provider = self.create_provider() - - response = self.client.post( - "/api/v1/shop/product-batches/", - { - "code": "B00001", - "product": self.product.pk, - "quantity": "5", - "provider": provider.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/product-batches/{pk}/") - assert response.status_code == status.HTTP_200_OK - - self.product.refresh_from_db() - assert self.product.stock == Decimal("5") - - response = self.client.delete(f"/api/v1/shop/product-batches/{pk}/") - assert response.status_code == status.HTTP_204_NO_CONTENT diff --git a/shop/tests/api/test_api_product_prices.py b/shop/tests/api/test_api_product_prices.py deleted file mode 100644 index 2f0d92c..0000000 --- a/shop/tests/api/test_api_product_prices.py +++ /dev/null @@ -1,116 +0,0 @@ -from decimal import Decimal - -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Product, ProductPrice, Tax - - -class TestProductPricessAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "productprice" - - def setUp(self): - self.create_user() - self.tax = Tax.objects.create( - code="IVA", - value=21, - ) - - def create_products(self): - self.product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - - ProductPrice.objects.create( - product=self.product, - price=Decimal("1.20"), - tax=self.tax, - ) - - def test_fetch_product_prices(self): - self.login() - self.create_products() - response = self.client.get(f"/api/v1/shop/products/{self.product.pk}/prices/") - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_product_prices(self): - self.login() - - product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - response = self.client.post( - f"/api/v1/shop/products/{product.pk}/prices/", - { - "price": "1.20", - "tax": self.tax.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/") - - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_update_product_prices(self): - self.login() - - product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - response = self.client.post( - f"/api/v1/shop/products/{product.pk}/prices/", - { - "price": "1.20", - "tax": self.tax.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - response = self.client.patch( - f"/api/v1/shop/products/{product.pk}/prices/{pk}/", - { - "price": "1.10", - }, - ) - assert response.status_code == status.HTTP_200_OK - response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/{pk}/") - assert response.data.get("price") == "1.10" - - def test_create_delete_product_prices(self): - self.login() - - product = Product.objects.create( - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - response = self.client.post( - f"/api/v1/shop/products/{product.pk}/prices/", - { - "price": "1.20", - "tax": self.tax.pk, - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.delete( - f"/api/v1/shop/products/{product.pk}/prices/{pk}/" - ) - assert response.status_code == status.HTTP_204_NO_CONTENT - - response = self.client.get(f"/api/v1/shop/products/{product.pk}/prices/{pk}/") - assert response.status_code == status.HTTP_404_NOT_FOUND diff --git a/shop/tests/api/test_api_products.py b/shop/tests/api/test_api_products.py deleted file mode 100644 index fef0fb3..0000000 --- a/shop/tests/api/test_api_products.py +++ /dev/null @@ -1,176 +0,0 @@ -import random -import string -from decimal import Decimal - -from django.utils import timezone -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Product, ProductBatch, ProductPrice, Tag, Tax - - -class TestProductsAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "product" - - def setUp(self): - self.create_user() - self.tax = Tax.objects.create( - code="IVA", - value=21, - ) - self.tag = Tag.objects.create(name="Alimentos") - - def get_random_sku(self): - return "".join(random.choices(string.ascii_uppercase, k=16)) - - def create_products(self): - self.product = Product.objects.create( - sku=self.get_random_sku(), - name="Papafritas", - description="Las mejores papafritas", - is_digital_asset=False, - url="", - ) - - self.product_price = ProductPrice.objects.create( - product=self.product, - price=Decimal("1.20"), - tax=self.tax, - date=timezone.now(), - ) - - self.batch = ProductBatch.objects.create( - code="B0001", - product=self.product, - quantity=Decimal("10"), - ) - - def test_fetch_products(self): - self.login() - self.create_products() - response = self.client.get("/api/v1/shop/products/") - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_product(self): - self.login() - response = self.client.post( - "/api/v1/shop/products/", - { - "sku": self.get_random_sku(), - "name": "Papafritas", - "description": "Las mejores papafritas", - "is_digital_asset": False, - "url": "", - "price": "1.20", - "stock": "100", - "tax": self.tax.pk, - "tags": [self.tag.pk], - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_product(self): - self.login() - response = self.client.post( - "/api/v1/shop/products/", - { - "sku": self.get_random_sku(), - "name": "Papafritas", - "description": "Las mejores papafritas", - "is_digital_asset": False, - "url": "", - "price": "1.20", - "stock": "100", - "tax": self.tax.pk, - "tags": [ - self.tag.pk, - ], - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch( - f"/api/v1/shop/products/{pk}/", {"name": "Papafritas pafritas"} - ) - assert response.status_code == status.HTTP_200_OK - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.data.get("name") == "Papafritas pafritas" - - def test_create_delete_product(self): - self.login() - response = self.client.post( - "/api/v1/shop/products/", - { - "sku": self.get_random_sku(), - "name": "Papafritas", - "description": "Las mejores papafritas", - "is_digital_asset": False, - "url": "", - "price": "1.20", - "stock": "100", - "tax": self.tax.pk, - "tags": [ - self.tag.pk, - ], - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.delete(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_204_NO_CONTENT - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_404_NOT_FOUND - - def test_create_cannot_delete_product_with_a_batch(self): - self.login() - self.create_products() - - response = self.client.get(f"/api/v1/shop/products/{self.product.pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.delete(f"/api/v1/shop/products/{self.product.pk}/") - assert response.status_code == status.HTTP_400_BAD_REQUEST - - def test_create_update_product_tags(self): - self.login() - tag = Tag.objects.create(name="Alimentación") - response = self.client.post( - "/api/v1/shop/products/", - { - "sku": self.get_random_sku(), - "name": "Papafritas", - "description": "Las mejores papafritas", - "is_digital_asset": False, - "url": "", - "price": "1.20", - "stock": "100", - "tax": self.tax.pk, - "tags": [ - self.tag.pk, - ], - }, - ) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch(f"/api/v1/shop/products/{pk}/", {"tags": [tag.pk]}) - assert response.status_code == status.HTTP_200_OK - response = self.client.get(f"/api/v1/shop/products/{pk}/") - assert response.data.get("tags", [])[0] == tag.pk diff --git a/shop/tests/api/test_api_providers.py b/shop/tests/api/test_api_providers.py deleted file mode 100644 index 72dbfbd..0000000 --- a/shop/tests/api/test_api_providers.py +++ /dev/null @@ -1,66 +0,0 @@ -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin - - -class TestProvidersAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "provider" - - def setUp(self): - self.create_user() - - def test_create_retrieve_provider(self): - self.login() - response = self.client.post( - f"/api/v1/shop/providers/", - { - "vat_id": "11111111H", - "name": "Darth Maul", - "email": "darth@maul.com", - "address": "Dathomir", - "city": "Dathomir", - "state": "Dathomir", - "country": "Dathomir", - "zip": "00001", - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/providers/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_provider(self): - self.login() - response = self.client.post( - f"/api/v1/shop/providers/", - { - "vat_id": "11111111H", - "name": "Darth Maul", - "email": "darth@maul.com", - "address": "Dathomir", - "city": "Dathomir", - "state": "Dathomir", - "country": "Dathomir", - "zip": "00001", - }, - ) - - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - response = self.client.get(f"/api/v1/shop/providers/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch( - f"/api/v1/shop/providers/{pk}/", - { - "address": "Mandalore", - }, - ) - assert response.status_code == status.HTTP_200_OK - response = self.client.get(f"/api/v1/shop/providers/{pk}/") - - assert response.status_code == status.HTTP_200_OK - assert response.data.get("address") == "Mandalore" diff --git a/shop/tests/api/test_api_tags.py b/shop/tests/api/test_api_tags.py deleted file mode 100644 index 8770013..0000000 --- a/shop/tests/api/test_api_tags.py +++ /dev/null @@ -1,50 +0,0 @@ -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Tag - - -class TestTagsAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "tag" - - def setUp(self): - self.create_user() - - def create_tags(self): - Tag.objects.create(name="Deportes") - - def test_fetch_taxes(self): - self.create_tags() - self.login() - response = self.client.get("/api/v1/shop/tags/") - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_retrieve_tax(self): - self.login() - response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/tags/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_tax(self): - self.login() - response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/tags/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch( - f"/api/v1/shop/tags/{pk}/", {"name": "Juegos de mesa"} - ) - assert response.status_code == status.HTTP_200_OK - - response = self.client.get(f"/api/v1/shop/tags/{pk}/") - assert response.status_code == status.HTTP_200_OK - - assert response.data.get("name") == "Juegos de mesa" diff --git a/shop/tests/api/test_api_taxes.py b/shop/tests/api/test_api_taxes.py deleted file mode 100644 index ea948d2..0000000 --- a/shop/tests/api/test_api_taxes.py +++ /dev/null @@ -1,54 +0,0 @@ -from rest_framework import status -from rest_framework.test import APITestCase - -from config.tests.mixins import TestUserAuthenticationMixin -from shop.models import Tax - - -class TestTaxesAPI(APITestCase, TestUserAuthenticationMixin): - model_name = "tax" - - def setUp(self): - self.create_user() - - def create_taxes(self): - Tax.objects.create( - code="IVA", - value=21, - ) - - def test_fetch_taxes(self): - self.create_taxes() - self.login() - response = self.client.get("/api/v1/shop/taxes/") - assert response.status_code == status.HTTP_200_OK - assert len(response.data.get("results")) == 1 - - def test_create_retrieve_tax(self): - self.login() - response = self.client.post("/api/v1/shop/taxes/", {"code": "IVA5", "value": 5}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/taxes/{pk}/") - assert response.status_code == status.HTTP_200_OK - - def test_create_update_tax(self): - self.login() - response = self.client.post("/api/v1/shop/taxes/", {"code": "IVA5", "value": 5}) - assert response.status_code == status.HTTP_201_CREATED - pk = response.data.get("id") - - response = self.client.get(f"/api/v1/shop/taxes/{pk}/") - assert response.status_code == status.HTTP_200_OK - - response = self.client.patch( - f"/api/v1/shop/taxes/{pk}/", {"code": "IVA10", "value": 10} - ) - assert response.status_code == status.HTTP_200_OK - - response = self.client.get(f"/api/v1/shop/taxes/{pk}/") - assert response.status_code == status.HTTP_200_OK - - assert response.data.get("code") == "IVA10" - assert response.data.get("value") == 10 diff --git a/shop/tests/api/test_product_filters.py b/shop/tests/api/test_product_filters.py deleted file mode 100644 index c604045..0000000 --- a/shop/tests/api/test_product_filters.py +++ /dev/null @@ -1,36 +0,0 @@ -from decimal import Decimal - -from django.test import TestCase - -from shop.filters import ProductFilter -from shop.models import Product -from shop.tests.mixins import CreateProductsMixin - - -class TestProductFilter(TestCase, CreateProductsMixin): - def setUp(self): - product_a = self.create_product( - name="producto 1", sku="001", price=Decimal("40.00") - ) - product_b = self.create_product( - name="producto 2", sku="002", price=Decimal("70.00") - ) - product_c = self.create_product( - name="producto 3", sku="003", price=Decimal("100.00") - ) - - def test_product_filter_by_price(self): - f = ProductFilter({"price_gt": "40"}) - f.is_valid() - qs = f.filter_queryset(Product.objects.all()) - assert qs.count() == 3 - - f = ProductFilter({"price_lt": "40"}) - f.is_valid() - qs = f.filter_queryset(Product.objects.all()) - assert qs.count() == 0 - - f = ProductFilter({"price_gt": "100"}) - f.is_valid() - qs = f.filter_queryset(Product.objects.all()) - assert qs.count() == 1 diff --git a/shop/tests/api/test_shop_models.py b/shop/tests/api/test_shop_models.py deleted file mode 100644 index 53f2658..0000000 --- a/shop/tests/api/test_shop_models.py +++ /dev/null @@ -1,102 +0,0 @@ -from decimal import Decimal - -from django.contrib.auth import get_user_model -from rest_framework.test import APITestCase as TestCase - -from shop.models import CustomerAddress, Product, ProductPrice, Tax -from shop.utils import create_order, create_order_line_for_product - -User = get_user_model() - - -class ShopModelsTest(TestCase): - def setUp(self) -> None: - self.tax = Tax.objects.create( - code="IVA", - value=21, - ) - self.customer = User.objects.create_user( - username="11111111H", - first_name="Darth", - last_name="Maull", - email="darth@maul.com", - password="dathomir", - ) - - self.customer_shipping_address = CustomerAddress.objects.create( - user=self.customer, - address="Dathomir", - address_town="Dathomir", - address_zip="00001", - address_state="Dathomir", - address_phone="900000000", - address_type=CustomerAddress.Types.SHIPPING, - ) - - self.customer_billing_address = CustomerAddress.objects.create( - user=self.customer, - address="Dathomir", - address_town="Dathomir", - address_zip="00001", - address_state="Dathomir", - address_phone="900000000", - address_type=CustomerAddress.Types.BILLING, - ) - self.create_products() - - def create_products(self): - self.potatoes = Product.objects.create( - sku="0000001", - name="Patatas", - stock=Decimal("100.00"), - ) - self.gasoline = Product.objects.create( - sku="0000002", - name="Gasolina", - stock=Decimal("800.00"), - ) - self.usb_c = Product.objects.create( - sku="0000003", - name="Cable USB-C", - stock=Decimal("5.00"), - ) - - ProductPrice.objects.create( - product=self.potatoes, price=Decimal("0.80"), tax=self.tax - ) - ProductPrice.objects.create( - product=self.gasoline, price=Decimal("1.15"), tax=self.tax - ) - ProductPrice.objects.create( - product=self.usb_c, price=Decimal("9.95"), tax=self.tax - ) - - def test_create_order(self): - order = create_order( - customer=self.customer, - billing_address=self.customer_billing_address.address, - billing_city=self.customer_billing_address.address_town, - billing_state=self.customer_billing_address.address_state, - billing_zip=self.customer_billing_address.address_zip, - billing_country=self.customer_billing_address.address_country, - ) - - l1 = create_order_line_for_product( - self.potatoes, - quantity=Decimal("1.5"), - order=order, - ) - l2 = create_order_line_for_product( - self.gasoline, - quantity=Decimal("40"), - order=order, - ) - l3 = create_order_line_for_product( - self.usb_c, - quantity=Decimal("1.00"), - order=order, - ) - order.calculate_total_from_lines() - - assert order.total == l1.total + l2.total + l3.total - assert order.base_total == l1.base_total + l2.base_total + l3.base_total diff --git a/users/api/__init__.py b/users/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/users/api/v1/__init__.py b/users/api/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/users/api/v1/serializers.py b/users/api/v1/serializers.py deleted file mode 100644 index a5aa9a9..0000000 --- a/users/api/v1/serializers.py +++ /dev/null @@ -1,88 +0,0 @@ -from typing import List - -from django.conf import settings -from django.contrib.auth import get_user_model -from django.contrib.auth.password_validation import ( - get_password_validators, - validate_password, -) -from rest_framework import serializers -from rest_framework.exceptions import ValidationError - -User = get_user_model() - - -AUTH_PASSWORD_VALIDATORS = getattr(settings, "AUTH_PASSWORD_VALIDATORS") - - -class UserSerializer(serializers.ModelSerializer): - permissions = serializers.SerializerMethodField() - - def get_permissions(self, obj: User) -> List[str]: - group_permissions = obj.get_group_permissions() - user_permissions = obj.get_user_permissions() - - return sorted(user_permissions.union(group_permissions)) - - class Meta: - model = User - fields = ( - "username", - "email", - "first_name", - "last_name", - "is_staff", - "is_superuser", - "permissions", - ) - read_only_fields = ( - "username", - "code", - "is_staff", - "is_superuser", - "permissions", - ) - - -class UpdatePasswordSerializer(serializers.ModelSerializer): - old_password = serializers.CharField(write_only=True) - new_password = serializers.CharField(write_only=True) - new_password2 = serializers.CharField(write_only=True) - - def validate_old_password(self, password): - user = self.context.get("request").user - - if not user.check_password(password): - raise ValidationError("Wrong old password") - - return password - - def validate_new_password(self, password): - old_password = self.context.get("request").data.get("old_password") - - if password == old_password: - raise ValidationError("Password can't be the same as the old one") - - password2 = self.context.get("request").data.get("new_password2") - if password != password2: - raise ValidationError("Password mismatch") - - validate_password( - password, - password_validators=get_password_validators(AUTH_PASSWORD_VALIDATORS), - ) - - return password - - def save(self, *args, **kwargs): - new_password = self.validated_data.get("new_password") - self.instance.set_password(new_password) - self.instance.save() - - class Meta: - model = User - fields = ( - "old_password", - "new_password", - "new_password2", - ) diff --git a/users/api/v1/urls.py b/users/api/v1/urls.py deleted file mode 100644 index c6ff781..0000000 --- a/users/api/v1/urls.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.urls import path -from rest_framework_simplejwt.views import ( - token_obtain_pair, - token_refresh, - token_verify, -) - -from users.api.v1.views import change_password, retrieve_update_me - -app_name = "auth" - - -urlpatterns = [ - path("login/", token_obtain_pair, name="login"), - path("refresh/", token_refresh, name="refresh_jwt"), - path("verify/", token_verify, name="verify_jwt"), - path("me/", retrieve_update_me, name="user_info"), - path("change-password/", change_password, name="change_password"), -] diff --git a/users/api/v1/views.py b/users/api/v1/views.py deleted file mode 100644 index f3687f8..0000000 --- a/users/api/v1/views.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.contrib.auth.models import User -from rest_framework.generics import RetrieveUpdateAPIView, UpdateAPIView -from rest_framework.permissions import IsAuthenticated - -from users.api.v1.serializers import UpdatePasswordSerializer, UserSerializer - - -class RetrieveUpdateMe(RetrieveUpdateAPIView): - """ - get: Return user info - - put: Update user info - """ - - serializer_class = UserSerializer - - def get_object(self): - return self.request.user - - -class ChangePassword(UpdateAPIView): - """ - put: Update user's password - """ - - serializer_class = UpdatePasswordSerializer - permission_classes = (IsAuthenticated,) - queryset = User.objects.all() - - def get_object(self): - return self.request.user - - -retrieve_update_me = RetrieveUpdateMe.as_view() -change_password = ChangePassword.as_view() diff --git a/users/tests/test_api_change_password.py b/users/tests/test_api_change_password.py deleted file mode 100644 index b3eee7e..0000000 --- a/users/tests/test_api_change_password.py +++ /dev/null @@ -1,110 +0,0 @@ -from django.contrib.auth import get_user_model -from django.shortcuts import reverse -from rest_framework import status -from rest_framework.test import APITestCase - -User = get_user_model() - - -class TestChangePassword(APITestCase): - def setUp(self) -> None: - self.password = "theonering" - self.user = User.objects.create( - username="sauron", - email="sauron@mordor.middleearth", - ) - - self.user.set_password(self.password) - self.user.save() - - def test_login_then_change_password(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}") - response = self.client.put( - reverse("auth:change_password"), - { - "new_password": "barad-dur", - "new_password2": "barad-dur", - "old_password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - - def test_login_then_change_password_with_wrong_password(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}") - response = self.client.put( - reverse("auth:change_password"), - { - "new_password": "barad-dur", - "new_password2": "barad-dur", - "old_password": "incorrectoldpassword", - }, - ) - - assert response.status_code == status.HTTP_400_BAD_REQUEST - - def test_login_then_change_password_mismatch_password(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}") - response = self.client.put( - reverse("auth:change_password"), - { - "new_password": "barad-dur", - "new_password2": "mountdoom", - "old_password": self.password, - }, - ) - - assert response.status_code == status.HTTP_400_BAD_REQUEST - - def test_login_then_change_password_but_its_the_same(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}") - response = self.client.put( - reverse("auth:change_password"), - { - "new_password": self.password, - "new_password2": self.password, - "old_password": self.password, - }, - ) - - assert response.status_code == status.HTTP_400_BAD_REQUEST diff --git a/users/tests/test_api_login.py b/users/tests/test_api_login.py deleted file mode 100644 index 093f305..0000000 --- a/users/tests/test_api_login.py +++ /dev/null @@ -1,101 +0,0 @@ -from django.contrib.auth import get_user_model -from django.shortcuts import reverse -from rest_framework import status -from rest_framework.test import APITestCase - -User = get_user_model() - - -class TestLogin(APITestCase): - def setUp(self) -> None: - self.password = "theonering" - self.user = User.objects.create( - username="sauron", - email="sauron@mordor.middleearth", - ) - - self.user.set_password(self.password) - self.user.save() - - def test_login(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - assert response.data.get("access") is not None - assert response.data.get("refresh") is not None - - def test_login_then_verify(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - response = self.client.post( - reverse("auth:verify_jwt"), - { - "token": jwt_token, - }, - ) - - assert response.status_code == status.HTTP_200_OK - - def test_login_then_refresh(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("refresh") - response = self.client.post( - reverse("auth:refresh_jwt"), - { - "refresh": jwt_token, - }, - ) - - assert response.status_code == status.HTTP_200_OK - assert response.data.get("access") is not None - assert response.data.get("access") != jwt_token - - def test_login_then_get_user_info(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": self.password, - }, - ) - - assert response.status_code == status.HTTP_200_OK - jwt_token = response.data.get("access") - self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {jwt_token}") - response = self.client.get(reverse("auth:user_info")) - - assert response.status_code == status.HTTP_200_OK - assert response.data.get("email") == self.user.email - - def test_login_failed(self): - response = self.client.post( - reverse("auth:login"), - { - "username": self.user.username, - "password": "wrongpassword", - }, - ) - - assert response.status_code == status.HTTP_401_UNAUTHORIZED