agateblue / django-dynamic-preferences

Dynamic global and instance settings for your django project
https://django-dynamic-preferences.readthedocs.org/
BSD 3-Clause "New" or "Revised" License
346 stars 86 forks source link

Mocking global preferences when testing #296

Closed uktamjon-komilov closed 11 months ago

uktamjon-komilov commented 1 year ago

dynamic_preferences_registry.py:

from dynamic_preferences.types import BooleanPreference
from dynamic_preferences.preferences import Section
from dynamic_preferences.registries import global_preferences_registry

authentication = Section("authentication")

@global_preferences_registry.register
class RegistrationOTPEnabledMode(BooleanPreference):
    section = authentication
    name = "registration_otp_enabled"
    default = False

global_preferences = global_preferences_registry.manager()

serializers.py:

from django.contrib.auth.models import User
from rest_framework import serializers

from .utils import generate_otp

class UserRegistrationLoginSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(required=False)
    password = serializers.CharField(required=False)

    def validate(self, attrs):
        attrs = super().validate(attrs)
        user = User.objects.filter(email=attrs["email"]).first()
        attrs["user"] = user
        return attrs

    def create(self, validated_data):
        user = validated_data.get("user", None)
        if user is not None:
            return user

        password = validated_data.get("password", None)
        if password is None:
            password = generate_otp(8)

        username = validated_data.get("username", None)
        if username is None:
            username = generate_otp(8)

        user = User.objects.create_user(
            email=validated_data["email"],
            username=username,
            password=password,
        )
        return user

views.py:

from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken

from .serializers import UserRegistrationLoginSerializer
from .dynamic_preferences_registry import global_preferences
from .tasks import send_otp_task

class UserRegistrationLoginView(generics.CreateAPIView):
    serializer_class = UserRegistrationLoginSerializer

    def create(self, request, *args, **kwargs):
        otp_enabled = global_preferences["authentication__registration_otp_enabled"]

        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        user = serializer.save()

        if otp_enabled:
            send_otp_task.delay(user.id)
            data = {"message": "An OTP has been sent to your email."}
            return Response(data, status=status.HTTP_200_OK)

        token = RefreshToken.for_user(user)
        data = {
            "message": "Login succeeded",
            "access": str(token.access_token),
            "refresh": str(token),
        }
        return Response(data, status=status.HTTP_201_CREATED)

tests.py:

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from django.contrib.auth.models import User

class UserRegistrationLoginViewTestCase(APITestCase):
    def setUp(self):
        self.url = reverse("user_registration_login")
        self.valid_payload = {"email": "test@test.com"}
        self.invalid_payload = {"email": "invalidemail"}

    ...

    def test_create_user_with_otp_enabled(self):
        with (...):
            response = self.client.post(self.url, data=self.valid_payload)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            self.assertTrue("message" in response.data)

    def test_create_user_with_otp_disabled(self):
        with (...):
            response = self.client.post(self.url, data=self.valid_payload)
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            self.assertTrue("access" in response.data)

How to mock global preferences when testing OTP enabled and disabled situations in the last tests?

agateblue commented 1 year ago

In tests.py you should be able to simply do something like

from dynamic_preferences_registry import global_preferences

class UserRegistrationLoginViewTestCase(APITestCase):

    def test_create_user_with_otp_enabled(self):
        global_preferences['authentication__registration_otp_enabled'] = True
        response = self.client.post(self.url, data=self.valid_payload)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue("message" in response.data)

    def test_create_user_with_otp_disabled(self):
        global_preferences['authentication__registration_otp_enabled'] = False
        response = self.client.post(self.url, data=self.valid_payload)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertTrue("access" in response.data)

Can you please try that and let me know if it works for you?

agateblue commented 11 months ago

Closing because of inactivity, feel free to reopen if my suggestion doesn't work