From 716c688e3e565bae2c8b73258e31a65e6c6aa6f1 Mon Sep 17 00:00:00 2001 From: Francis Masha Date: Mon, 3 Dec 2018 22:45:23 +0300 Subject: [PATCH] - write unit tests for the errors - implement the error messages on the register api [Finishes #161967009] --- authors/apps/authentication/serializers.py | 52 +++++++- .../authentication/tests/test_registration.py | 123 +++++++++++++++++- 2 files changed, 166 insertions(+), 9 deletions(-) diff --git a/authors/apps/authentication/serializers.py b/authors/apps/authentication/serializers.py index bf1e8a1..e0a4874 100755 --- a/authors/apps/authentication/serializers.py +++ b/authors/apps/authentication/serializers.py @@ -1,6 +1,7 @@ from django.contrib.auth import authenticate from rest_framework import serializers +from rest_framework.validators import UniqueValidator from authors.apps.authentication.backends import JWTAuthentication from .models import User @@ -9,13 +10,58 @@ class RegistrationSerializer(serializers.ModelSerializer): """Serializers registration requests and creates a new user.""" + def __init__(self, *args, **kwargs): + super(RegistrationSerializer, self).__init__(*args, **kwargs) + + # Override the default error_messages with a custom field error + for field in self.fields: + error_messages = self.fields[field].error_messages + error_messages['null'] = error_messages['blank'] \ + = error_messages['required'] \ + = 'Please fill in the {}.'.format(field) + + # Ensure the username entered is unique and has a descriptive error message + # when a duplicate username is entered and an invalid username. + username = serializers.RegexField( + regex='^(?!.*\ )[A-Za-z\d\-\_]+$', + min_length=4, + max_length=30, + required=True, + validators=[UniqueValidator( + queryset=User.objects.all(), + message='The username already exists. Kindly try another.' + )], + error_messages={ + 'min_length': 'Username must have a minimum of 4 characters.', + 'max_length': 'Username must have a maximum of 30 characters.', + 'invalid': 'Username cannot have blank spaces.' + } + ) + + # Ensure the email entered is unique and has a descriptive error message + # when a duplicate email is entered and an invalid email address. + email = serializers.EmailField( + validators=[UniqueValidator( + queryset=User.objects.all(), + message='User with this email already exists. Sign in instead or try another.' + )], + error_messages={ + 'invalid': 'You have input an invalid email. Kindly check again.' + } + ) + # Ensure passwords are at least 8 characters long, no longer than 128 # characters, and can not be read by the client. - password = serializers.CharField( + password = serializers.RegexField( + regex=r'^(?=.*[a-zA-Z])(?=.*[0-9]).*', max_length=128, min_length=8, - write_only=True - ) + write_only=True, + error_messages={ + 'max_length': 'Password must have a maximum of 128 characters.', + 'min_length': 'Password must have a minimum of 8 characters.', + 'invalid': 'Password must contain a letter and a number.' + }) token = serializers.SerializerMethodField() refresh_token = serializers.SerializerMethodField() diff --git a/authors/apps/authentication/tests/test_registration.py b/authors/apps/authentication/tests/test_registration.py index fefd8c2..b8ba831 100644 --- a/authors/apps/authentication/tests/test_registration.py +++ b/authors/apps/authentication/tests/test_registration.py @@ -1,5 +1,9 @@ +import random +import string + import pytest from faker import Factory +from rest_framework import status from rest_framework.reverse import reverse from authors.apps.authentication.models import User @@ -22,26 +26,26 @@ class TestRegistration: } def test_user_instance(self): - """ - test user model methods - :return: - """ + """Test user model methods""" assert self.test_user.__str__() == self.test_user.email assert self.test_user.get_full_name == self.test_user.username assert self.test_user.get_short_name() == self.test_user.username def test_create_super_user(self): + """Test super user can be created""" su = User.objects.create_superuser(**self.user['user']) assert su.is_active is True assert su.is_staff is True assert su.is_superuser is True def test_user_registration(self, test_client): + """Test successful user registration""" response = test_client.post(reverse('register'), data=self.user, format='json') assert response.status_code is 201 assert User.objects.count() > 0 def test_token_in_response(self, test_client): + """Test token is generated and is at the response""" response = test_client.post(reverse('register'), self.user, format='json') assert isinstance(response.data, dict) @@ -49,11 +53,118 @@ def test_token_in_response(self, test_client): assert 'token' in response.data def test_add_existing_user(self, test_client): + """Test API cannot add an existing user""" response = test_client.post(reverse('register'), self.user, format='json') response2 = test_client.post(reverse('register'), self.user, format='json') assert response2.status_code == 400 assert 'errors' in response2.data - assert response2.data['errors']['email'][0] == "user with this email already exists." - assert response2.data['errors']['username'][0] == "user with this username already exists." + assert response2.data['errors']['email'][0] == "User with this email already exists. Sign in instead or try " \ + "another." + assert response2.data['errors']['username'][0] == "The username already exists. Kindly try another." + + def test_user_cannot_register_with_a_blank_email_field(self, test_client): + """Test the API cannot register a user with blank password field""" + del self.user['user']['email'] + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['email'][0] == "Please fill in the email." + + def test_user_cannot_register_with_an_invalid_email(self, test_client): + """Test the API cannot register a user with an invalid email""" + self.user['user']['email'] = 'WRONG_EMAIL' + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['email'][0] == "You have input an invalid email. Kindly check again." + + def test_user_cannot_register_with_a_short_username(self, test_client): + """Test the API cannot register a user with a short username""" + self.user['user']['username'] = 'abc' + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['username'][0] == "Username must have a minimum of 4 characters." + + def test_user_cannot_register_with_a_long_username(self, test_client): + """Test the API cannot register a user with a long password""" + chars = string.ascii_letters + string.digits + uname_size = 31 + username = ''.join((random.choice(chars)) for x in range(uname_size)) + self.user['user']['username'] = username + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['username'][0] == "Username must have a maximum of 30 characters." + + def test_user_cannot_register_with_a_blank_username_field(self, test_client): + """Test the API cannot register a user with blank username field""" + del self.user['user']['username'] + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['username'][0] == "Please fill in the username." + + def test_user_cannot_register_with_wrong_username(self, test_client): + """Test the API cannot register a user with an wrong username format""" + self.user['user']['username'] = 'wrong username' + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['username'][0] == "Username cannot have blank spaces." + + def test_user_cannot_register_with_a_blank_password_field(self, test_client): + """Test the API cannot register a user with blank password field""" + del self.user['user']['password'] + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['password'][0] == "Please fill in the password." + + def test_user_cannot_register_with_a_short_password(self, test_client): + """Test the API cannot register a user with a short password""" + self.user['user']['password'] = 'abc' + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['password'][0] == "Password must have a minimum of 8 characters." + + def test_user_cannot_register_with_a_long_password(self, test_client): + """Test the API cannot register a user with a long password greater than 128 characters""" + chars = string.ascii_letters + string.digits + pwd_size = 129 + password = ''.join((random.choice(chars)) for x in range(pwd_size)) + self.user['user']['password'] = password + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['password'][0] == "Password must have a maximum of 128 characters." + + def test_unsuccessful_registration_due_to_all_letters_password(self, test_client): + """Test the API cannot register a user with all letters password""" + self.user['user']['password'] = 'abcdefgh' + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['password'][0] == "Password must contain a letter and a number." + + def test_unsuccessful_registration_due_to_all_numeric_password(self, test_client): + """Test the API cannot register a user with all numeric password""" + self.user['user']['password'] = 12345678 + response = test_client.post(reverse('register'), self.user, format='json') + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'errors' in response.data + assert response.data['errors']['password'][0] == "Password must contain a letter and a number."