diff --git a/docker-compose.yml b/docker-compose.yml index 840bbb7..20f698e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: db: image: postgres + volumes: + - postgres_data:/var/lib/postgresql/data acacia-back: container_name: acacia_backend @@ -25,4 +27,7 @@ services: volumes: - .:/code depends_on: - - db \ No newline at end of file + - db + +volumes: + postgres_data: diff --git a/requirements.txt b/requirements.txt index ab4efe4..4e4ab77 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -Django==2.2 +Django==2.2.4 djangorestframework==3.10 django-phonenumber-field==3.0 phonenumbers==8.10 -djangorestframework_simplejwt -django-cors-headers psycopg2==2.8.3 +django-cors-headers==3.1.0 +djangorestframework-simplejwt coverage diff --git a/src/acacia/settings.py b/src/acacia/settings.py index 8d4b193..579560b 100644 --- a/src/acacia/settings.py +++ b/src/acacia/settings.py @@ -14,6 +14,8 @@ from scripts.wait_for_db import start_services from django.utils.translation import ugettext_lazy as _ +from .wait_db import start_services + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -41,6 +43,7 @@ 'django.contrib.staticfiles', # libs + 'phonenumber_field', 'rest_framework', 'rest_framework.authtoken', 'corsheaders', @@ -92,6 +95,7 @@ } } +# STARTS SERVICES THAT DJANGO DEPENDS E.G. postgres start_services() LANGUAGES = ( @@ -146,6 +150,8 @@ ] } +# CORS headers to responses + CORS_ORIGIN_WHITELIST = [ "http://localhost:8080", "http://localhost:8000", diff --git a/src/acacia/wait_db.py b/src/acacia/wait_db.py new file mode 100644 index 0000000..fdf9b2e --- /dev/null +++ b/src/acacia/wait_db.py @@ -0,0 +1,56 @@ +import importlib +import os +import time + +import logging + +SERVICES_STARTED = False + +log = logging.getLogger('ej') + + +def start_services(): + global SERVICES_STARTED + + if SERVICES_STARTED: + return + + start_postgres() + + SERVICES_STARTED = True + + +def start_postgres(): + # settings_path = os.environ['DJANGO_SETTINGS_MODULE'] + # settings = importlib.import_module(settings_path) + + # db = settings.DATABASES['default'] + dbname = 'postgres' # db['NAME'] + user = 'postgres' # db['USER'] + password = '' # db['PASSWORD'] + host = 'db' # db['HOST'] + + for _ in range(100): + if can_connect(dbname, user, password, host): + log.info("Postgres is available. Continuing...") + return + log.warning('Postgres is unavailable. Retrying in 0.5 seconds') + time.sleep(0.5) + + log.critical('Maximum number of attempts connecting to postgres database') + raise RuntimeError('could not connect to database') + + +def can_connect(dbname, user, password, host): + import psycopg2 + + try: + psycopg2.connect( + dbname=dbname, + user=user, + password=password, + host=host + ) + except psycopg2.OperationalError: + return False + return True diff --git a/src/users/serializers.py b/src/users/serializers.py index b5ed607..ac78c53 100644 --- a/src/users/serializers.py +++ b/src/users/serializers.py @@ -19,6 +19,8 @@ class UserSignUpSerializer(serializers.Serializer): email = serializers.EmailField( required=True, + validators=[UniqueValidator(queryset=User.objects.all())], + #unique=True, label="Email Address", ) @@ -32,39 +34,40 @@ class UserSignUpSerializer(serializers.Serializer): confirm_password = serializers.CharField( write_only=True, required=True, - label="Confirm Password", - style={'input_type': 'password'} + label="Confirm Password", + style={'input_type': 'password'} ) class Meta: model = User fields = ['username', 'email', 'password', 'confirm_password'] + #def validate_email(self, email): + # if User.objects.filter(email=email).exists(): + # raise serializers.ValidationError('Email já cadastrado') + # return email + + def validate_password(self, password): min_length = getattr(settings, 'PASSWORD_MIN_LENGTH', 8) if len(password) < min_length: raise serializers.ValidationError( - 'Password should be atleast %s characters long.' % (min_length) + 'A senha deve ter no mínimo %s caracteres' % (min_length) ) return password - def validate_email(self, email): - if User.objects.filter(email=email).exists(): - raise serializers.ValidationError('Email already exists.') - return email - - def validate_username(self, username): - if User.objects.filter(username=username).exists(): - raise serializers.ValidationError('Username already exists.') - return username - def validate_confirm_password(self, password_confirmation): data = self.get_initial() password = data.get('password') if password != password_confirmation: - raise serializers.ValidationError('Passwords must match.') + raise serializers.ValidationError('As senhas devem corresponder') return password_confirmation + def validate_username(self, username): + if User.objects.filter(username=username).exists(): + raise serializers.ValidationError('Usuário com este nome já cadastrado') + return username + def create(self, validated_data): # this fields dont belongs to this class diff --git a/src/users/tests.py b/src/users/tests.py index 0ec3a29..dff5b7b 100644 --- a/src/users/tests.py +++ b/src/users/tests.py @@ -2,8 +2,109 @@ from rest_framework.test import APITestCase from django.urls import reverse +from django.db import IntegrityError +class UserRegistrationAPIViewTestCase(APITestCase): + url = reverse('users:register') + + def test_different_password_on_password_confirmation(self): + """ + Test to try to create a user with a wrong verification password + """ + + user_data = { + "username": "vitas", + "email": "vitas@iAmGreat.com", + "password": "VitasIsNice", + "confirm_password": "VitasIsAwesome" + } + + response = self.client.post(self.url, user_data) + self.assertEqual(400, response.status_code) + + + def test_password_less_than_8_characters(self): + """ + Test to try to create a user with a password less than 8 characters + """ + + user_data = { + "username": "vitas", + "email": "vitas@iAmGreat.com", + "password": "HiVitas", + "confirm_password": "HiVitas" + } + + response = self.client.post(self.url, user_data) + self.assertEqual(400, response.status_code) + + + def test_unique_email_validation(self): + """ + Test to try to create a user with a registered email + """ + + user1_data = { + "username": "vitas", + "email": "vitas@iAmGreat.com", + "password": "VitasIsAwesome", + "confirm_password": "VitasIsAwesome" + } + user2_data = { + "username": "Reanu_Reves", + "email": "vitas@iAmGreat.com", + "password": "cyberpunk2077", + "confirm_password": "cyberpunk2077" + } + response = self.client.post(self.url, user1_data) + self.assertEqual(201, response.status_code) + with self.assertRaises(IntegrityError): + response = self.client.post(self.url, user2_data) + + + def test_unique_username_validation(self): + """ + Test to try to create a user with a registered username + """ + + user_data = { + "username": "vitas", + "email": "vitas@iAmGreat.com", + "password": "VitasIsAwesome", + "confirm_password": "VitasIsAwesome" + } + + response = self.client.post(self.url, user_data) + self.assertEqual(201, response.status_code) + + user_data = { + "username": "vitas", + "email": "keanu@reeves.com", + "password": "cyberpunk2077", + "confirm_password": "cyberpunk2077" + } + + response = self.client.post(self.url, user_data) + self.assertEqual(400, response.status_code) + + + def test_user_registration(self): + """ + Test to create a user with valid data + """ + + user_data = { + "username": "keanu_reeves", + "email": "keanu@reeves.com", + "password": "cyberpunk2077", + "confirm_password": "cyberpunk2077" + } + + response = self.client.post(self.url, user_data) + self.assertEqual(201, response.status_code) + + class UserAuthenticationAPIViewTestCase(APITestCase): # SETUP