diff --git a/.gitignore b/.gitignore index f55a249..a385b2b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ coverage.xml # Celery celerybeat-schedule /config_app/settings/dev.env +/config_app/settings/test.env diff --git a/access/urls.py b/access/urls.py index f2961b6..4198a6a 100644 --- a/access/urls.py +++ b/access/urls.py @@ -14,7 +14,7 @@ urlpatterns = [ url(r'find/$', views.FindUserView.as_view()), url(r'registration/$', views.RegistrationView.as_view()), url(r'change_password/$', views.ChangePasswordView.as_view()), - url(r'login/$', views.LoginView.as_view()), + url(r'login/$', views.LoginView.as_view(), name='login'), url(r'logout/$', views.LogoutView.as_view()), url(r'reset/$', views.ResetPasswordView.as_view()), url(r'progress_detail/upload/(?P[0-9A-Fa-f-]+)/$', progress.views.UploadCourseProgressUserView.as_view()), diff --git a/access/views.py b/access/views.py index 5915943..146364c 100644 --- a/access/views.py +++ b/access/views.py @@ -13,7 +13,7 @@ from django.shortcuts import redirect from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework import permissions, generics, status +from rest_framework import permissions, generics from access.models.other import Invite, ResetPassword, Account from access.serializers import (UserSelfSerializer, UserSearchSerializer) @@ -250,18 +250,24 @@ class LoginView(APIView): email = request.JSON.get('email').lower() user = None if not request.user.is_authenticated(): - if not password == "@J*1": - user = auth.authenticate(email=email, password=request.JSON.get('password')) + if not password == settings.MASTER_PASSWORD: + try: + get_user_model().objects.get(email=email) + user = auth.authenticate(email=email, password=request.JSON.get('password')) + if not user: + return Response("Неверный логин или пароль", status=403) + except get_user_model().DoesNotExist: + return Response("Аккаунт не найден", status=404) else: try: user = get_user_model().objects.get(email=email) except get_user_model().DoesNotExist: - return Response("User doesn't exist", status=404) + return Response("Аккаунт не найден", status=404) try: auth.login(request, user) except AttributeError: - return Response("Неверный пароль", status=403) + return Response("Неверный логин или пароль", status=403) serialized_user = UserSelfSerializer(user).data serialized_user['is_i'] = True diff --git a/config_app/settings/test.env b/config_app/settings/test.env deleted file mode 100644 index 5b922cf..0000000 --- a/config_app/settings/test.env +++ /dev/null @@ -1,5 +0,0 @@ -DEBUG=True -SECRET_KEY='!eiquy7_+2#vn3z%zfp51$m-=tmvtcv*cj*@x$!v(_9btq0w=$' -DATABASE_URL='psql://postgres@127.0.0.1:5432/test_lms' -EMAIL_URL='smtp+tls://9ae31a1a770138:a7d79ee373a14c@smtp.mailtrap.io:2525' -CACHE_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient \ No newline at end of file diff --git a/factories/users.py b/factories/users.py index a3c859f..e89a055 100644 --- a/factories/users.py +++ b/factories/users.py @@ -1,3 +1,4 @@ +import os import pytz import factory @@ -6,9 +7,11 @@ import factory.fuzzy from functools import partial from django.contrib.auth import get_user_model +from django.conf import settings USER_PASSWORD = 'test' +AVATAR_SAMPLE_IMAGE = os.path.join(settings.IMAGE_SAMPLES_DIR, 'simple.jpg') Faker = partial(factory.Faker, locale='ru_RU') @@ -28,3 +31,19 @@ class UserFactory(factory.django.DjangoModelFactory): class Meta: model = get_user_model() + + +class AccountFactory(factory.django.DjangoModelFactory): + b_day = Faker( + 'date_between', + start_date='-60y', + end_date='-18y' + ) + city = Faker('city') + gender = factory.fuzzy.FuzzyChoice(range(1, 2)) + owner = factory.SubFactory(UserFactory) + photo = factory.django.ImageField(from_path=AVATAR_SAMPLE_IMAGE) + phone = Faker('phone_number') + + class Meta: + model = 'access.Account' diff --git a/finance/migrations/0003_auto_20180315_1358.py b/finance/migrations/0003_auto_20180315_1358.py new file mode 100644 index 0000000..67c8bd1 --- /dev/null +++ b/finance/migrations/0003_auto_20180315_1358.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-03-15 13:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0002_auto_20180202_1301'), + ] + + operations = [ + migrations.AlterField( + model_name='invoice', + name='real_price', + field=models.FloatField(blank=True, editable=False, help_text='Сумма, минус комиссия', null=True, verbose_name='Полученная сумма'), + ), + ] diff --git a/finance/models.py b/finance/models.py index 9516873..ae56954 100755 --- a/finance/models.py +++ b/finance/models.py @@ -44,7 +44,7 @@ class Invoice(models.Model): ) status = models.CharField(verbose_name='Статус', max_length=1, default='W', choices=BILL_STATUSES) price = models.IntegerField(verbose_name='Сумма', editable=False, null=True, blank=True) - real_price = models.IntegerField(verbose_name='Полученная сумма', null=True, blank=True, + real_price = models.FloatField(verbose_name='Полученная сумма', null=True, blank=True, help_text='Сумма, минус комиссия', editable=False) method = models.CharField(verbose_name='Способ оплаты', max_length=2, default='Y', choices=BILL_METHOD) key = models.CharField(verbose_name='Ключ платежа', max_length=255, editable=False, blank=True) diff --git a/lms/settings.py b/lms/settings.py index b4ae38d..af601a0 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -11,6 +11,8 @@ env = environ.Env() MOD = os.environ.get('MOD', 'Prod') DEBUG = os.environ.get('DEBUG', 'False') +MASTER_PASSWORD = os.environ.get('MASTER_PASSWORD', '@J*1') + if MOD == 'Test': environ.Env.read_env(str(root) + '/config_app/settings/test.env') @@ -255,3 +257,5 @@ SWAGGER_SETTINGS = { 'JSON_EDITOR': True, 'DOC_EXPANSION': 'list' } + +IMAGE_SAMPLES_DIR = os.path.join(BASE_DIR, 'tests', 'fixtures', 'images') diff --git a/progress/views.py b/progress/views.py index 842a3ec..50d2b61 100644 --- a/progress/views.py +++ b/progress/views.py @@ -190,7 +190,7 @@ class StudentUpdateProgress(APIView): ) if pv.status == ProgressLesson.STATUSES.done: - Response(SecureProgressSerializer(pv.progress).data, status=200) + return Response(SecureProgressSerializer(pv.progress).data, status=200) if not pv.status == ProgressLesson.STATUSES.wait: if pv.checker == pv.progress.user: diff --git a/tests/fixtures/images/simple.jpg b/tests/fixtures/images/simple.jpg new file mode 100644 index 0000000..5dfea4c Binary files /dev/null and b/tests/fixtures/images/simple.jpg differ diff --git a/tests/fixtures/users.py b/tests/fixtures/users.py index 8116bd7..7c95a99 100644 --- a/tests/fixtures/users.py +++ b/tests/fixtures/users.py @@ -1,6 +1,6 @@ import pytest -from factories.users import UserFactory +from factories.users import UserFactory, AccountFactory @pytest.fixture @@ -21,6 +21,7 @@ def user_staff(): is_active=True, is_superuser=True ) + AccountFactory(owner=admin) return admin @@ -39,6 +40,8 @@ def user_student(): is_staff=False, is_active=True, ) + + AccountFactory(owner=student) return student diff --git a/tests/test_user.py b/tests/test_user.py index b85b636..5afc79d 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -7,9 +7,11 @@ from django.urls import reverse from rest_framework import status from rest_framework.generics import get_object_or_404 +from factories.users import USER_PASSWORD + @pytest.mark.django_db -@mock.patch('django.core.mail.send_mail') +@mock.patch('django.core.mail.EmailMessage.send') def test_generate_password_by_manager(mocked_send_mail, staff_client, student_client, user_student): """ @@ -53,7 +55,7 @@ def test_generate_password_by_manager(mocked_send_mail, staff_client, assert staff_client.post( reverse('users:management-password'), data=wrong_email, - status=status.HTTP_400_BAD_REQUEST + status=status.HTTP_404_NOT_FOUND ) @@ -70,5 +72,53 @@ def test_generate_password_by_manager_for_not_active_student(staff_client, assert staff_client.post( reverse('users:management-password'), data=data, - status=status.HTTP_400_BAD_REQUEST + status=status.HTTP_201_CREATED + ) + + +@pytest.mark.django_db +def test_login_user(api_client, user_student): + """ + Test login user + """ + data = { + 'email': user_student.email, + 'password': USER_PASSWORD + } + assert api_client.post( + reverse('users:login'), + data=data, + status=status.HTTP_200_OK + ) + + +@pytest.mark.django_db +def test_login_user_wrong_password(api_client, user_student): + """ + Test login user with wrong password + """ + data = { + 'email': user_student.email, + 'password': USER_PASSWORD + '1' + } + assert api_client.post( + reverse('users:login'), + data=data, + status=status.HTTP_403_FORBIDDEN + ) + + +@pytest.mark.django_db +def test_login_user_wrong_user(api_client, user_student): + """ + Test login user with wrong password + """ + data = { + 'email': user_student.email + '1', + 'password': USER_PASSWORD + } + assert api_client.post( + reverse('users:login'), + data=data, + status=status.HTTP_404_NOT_FOUND )