import datetime import json import logging from datetime import date, timedelta from enum import Enum import pytest from django.contrib.auth.models import User from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ValidationError from django.urls import reverse from rest_framework import status from console.tasks import expire_users from core.utils import dtnow from perms.models import Perm from users.models import UserInfo from users.services.userinfo import UserStatusService, InUserInfoStrategy _log = logging.getLogger(__name__) class MethodEnum(Enum): GET = 0 HEAD = 1 POST = 3 PUT = 4 DELETE = 5 OPTIONS = 6 TRACE = 7 PATCH = 8 @pytest.fixture def receive_url(client): def _get(url, method=MethodEnum.GET, **kwargs): if method == MethodEnum.GET: return client.get(url, **kwargs) elif method == MethodEnum.HEAD: return client.head(url, **kwargs) elif method == MethodEnum.POST: return client.post(url, **kwargs, content_type="application/json") elif method == MethodEnum.PUT: return client.put(url, **kwargs, content_type="application/json") elif method == MethodEnum.DELETE: return client.delete(url, **kwargs) elif method == MethodEnum.OPTIONS: return client.options(url, **kwargs) elif method == MethodEnum.TRACE: return client.trace(url, **kwargs) elif method == MethodEnum.PATCH: return client.patch(url, **kwargs, content_type="application/json") raise NotImplemented(f'Method {method} not implemented') return _get @pytest.fixture() def create_user_info(): """Return new user with userinfo""" user = User.objects.create(username="Max", first_name="Pane", is_active=True) UserInfo.objects.create(user=user, comment="New user Max Pane") @pytest.fixture() def create_users(): users_list = [] for item in range(5): if item % 2 == 0: username = f'deleted_{item}' user = User.objects.create_user(username=username, is_active=False) else: username = f'user_{item}' user = User.objects.create_user(username=username, is_active=False) users_list.append(user) for user in users_list: UserInfo.objects.create(user=user, comment=f'{user.username}') @pytest.mark.django_db @pytest.fixture def can_open_url(client, receive_url, add_user_with_permissions): def _open(url: str, permissions: list, method=MethodEnum.GET, **kwargs): username = 'user' password = 'test' user = add_user_with_permissions(username=username, password=password) client.force_login(user) if permissions: response = receive_url(url=url, method=method, **kwargs) assert response.status_code == status.HTTP_403_FORBIDDEN user_1 = add_user_with_permissions(username=username + '1', password=password, permissions=permissions) client.force_login(user_1) response = receive_url(url=url, method=method, **kwargs) assert response.status_code != status.HTTP_403_FORBIDDEN return _open test_args = [ ('active-users-list', [MethodEnum.GET], [Perm.can_view_user_list]), ('active-users-list', [MethodEnum.POST], [Perm.can_add_user]), ('active-users-detail', [MethodEnum.GET], [Perm.can_view_user]), ('active-users-detail', [MethodEnum.PUT], [Perm.can_edit_user]), ('active-users-detail', [MethodEnum.PATCH], [Perm.can_edit_user]), ] @pytest.mark.django_db class TestUsers: url_name_and_url_kwargs_map = {} url_data_map = {} # data for POST, PATCH, PUT @pytest.fixture(autouse=True) def setup_test(self, add_user_with_permissions): self.user = add_user_with_permissions(username='test_username1', password='testpwd', is_superuser=True) self.url_name_and_url_kwargs_map['active-users-detail'] = {'pk': 1} @pytest.mark.unit def test_api_list(self, client, add_user_with_permissions, create_users): user_count = User.objects.count() user = add_user_with_permissions(username='user0', password='passwd12', is_superuser=True) url = reverse('active-users-list') client.force_login(user) response = client.get(url) data_json = response.json() assert response.status_code == 200 assert len(data_json) == 4 assert len(data_json) != user_count # Test skipped, because we have no permissions @pytest.mark.skip @pytest.mark.unit @pytest.mark.parametrize("url, methods, permissions", test_args) def test_permissions(self, url: str, methods: list, permissions: list, client, receive_url, get_url, add_user_with_permissions, can_open_url): url_kwargs = self.url_name_and_url_kwargs_map.get(url) if url_kwargs: _url = get_url(url, kwargs=url_kwargs) else: _url = get_url(url) data = self.url_data_map.get(url, {}) for method in methods: can_open_url(url=_url, method=method, permissions=permissions, **data) @pytest.mark.unit def test_user_details(self, client, add_user_with_permissions, create_user_info): user = add_user_with_permissions(username='user0', password='passwd12', is_superuser=True) url = reverse('active-users-detail', kwargs={"pk": User.objects.get(username='Max').pk}) client.force_login(user) response = client.get(url) data_json = response.json() assert response.status_code == 200 assert data_json["user"]["username"] == "Max" assert data_json["user"]["is_active"] == True assert data_json["user"]["first_name"] == "Pane" @pytest.mark.unit def test_add_user(self, client, add_user_with_permissions, create_user_info): url = reverse('active-users-list') client.force_login(self.user) users_before = User.objects.count() data = { "user": { "username": "swaggeruserYYYYMMDD", "first_name": "swagger_name", "is_active": True, "email": "swagger@mail.ru", "password": "IamTheKingOfTheWorld1" }, "comment": "asdasd", "timezone": "Europe/Moscow", "expire_date": "2030-10-10" } response = client.post(url, data=json.dumps(data), content_type="application/json") assert response.status_code == 201 users_after = User.objects.count() assert users_before != users_after new_user = User.objects.get(username="swaggeruserYYYYMMDD") info = UserInfo.objects.get(user=new_user) assert new_user.first_name == 'swagger_name' assert new_user.email == "swagger@mail.ru" assert info.comment == "asdasd" assert info.timezone == "Europe/Moscow" assert new_user.is_superuser assert new_user.is_staff assert info.expire_date == datetime.date(2030, 10, 10) client.logout() response_ = client.post(reverse('api_login'), {"username": "swaggeruserYYYYMMDD", "password": "IamTheKingOfTheWorld1"}) assert response_.wsgi_request.user.is_authenticated url = reverse('active-users-detail', kwargs={"pk": User.objects.get(username='Max').pk}) response = client.get(url) assert response.status_code == 200 @pytest.mark.unit def test_add_user_bad_password(self, client, add_user_with_permissions, create_user_info): url = reverse('active-users-list') client.force_login(self.user) users_before = User.objects.count() data = { "user": { "username": "swagger_user_YYYY-MM-DD", "first_name": "swagger_name", "is_active": True, "email": "swagger@mail.ru", "password": "password" }, "comment": "asdasd", "timezone": "Europe/Moscow", "expire_date": "2030-10-10" } response = client.post(url, data=json.dumps(data), content_type="application/json") assert response.status_code == 400 assert users_before == User.objects.count() @pytest.mark.unit def test_add_user_bad_expire_date(self, client, add_user_with_permissions, create_user_info): url = reverse('active-users-list') client.force_login(self.user) data = { "user": { "username": "userBadExpDate", "first_name": "swagger_name", "is_active": True, "email": "swagger@mail.ru", "password": "IamTheKingOfTheWorld1" }, "comment": "asdasd", "timezone": "Europe/Moscow", "expire_date": "2000-10-10" } before = User.objects.count() response = client.post(url, data=json.dumps(data), content_type="application/json") assert response.status_code == 400 assert before == User.objects.count() @pytest.mark.unit def test_add_user_empty_first_name(self, api_client): url = reverse('active-users-list') api_client.force_authenticate(self.user) data = { "user": { "username": "userBadExpDate", "first_name": "", "is_active": True, "email": "swagger@mail.ru", "password": "IamTheKingOfTheWorld1" }, "comment": "asdasd", "timezone": "Europe/Moscow", "expire_date": "2000-10-10" } before = User.objects.count() response = api_client.post(url, data=data, format='json') assert response.status_code == status.HTTP_400_BAD_REQUEST assert 'first_name' in response.json()['user'] assert response.json()['user']['first_name'] == ['This field may not be blank.'] assert before == User.objects.count() @pytest.mark.unit def test_add_user_with_too_long_username(self, api_client, add_user_with_permissions): url = reverse('active-users-list') api_client.force_authenticate(self.user) long_username = 'a' * 150 data = { "user": { "username": long_username, "first_name": "swagger_name", "is_active": True, "email": "swagger@mail.ru", "password": "IamTheKingOfTheWorld1" }, "comment": "asdasd", "timezone": "Europe/Moscow", } response = api_client.post(url, data=data, format="json") assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()['user']['username'] == ["Ensure this field has no more than 131 characters."] normal_username = 'a' * 131 data['user']['username'] = normal_username response = api_client.post(url, data=data, format="json") print(response.json()) assert response.status_code == status.HTTP_201_CREATED @pytest.mark.unit def test_edit_user_patch(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = {"user": {"first_name": "user"}, "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json", format="json") edited_customer = User.objects.get(pk=customer.pk) info = UserInfo.objects.get(user=edited_customer) assert response.status_code == 200 assert edited_customer.username == 'Max' assert edited_customer.first_name == "user" assert edited_customer.is_active @pytest.mark.unit def test_edit_user_patch_good_expire_date(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = {"user": {"first_name": "user"}, "expire_date": date.today() + timedelta(days=1), "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json", format="json") edited_customer = User.objects.get(pk=customer.pk) info = UserInfo.objects.get(user=edited_customer) assert response.status_code == 200 assert edited_customer.username == 'Max' assert edited_customer.first_name == "user" assert edited_customer.is_active @pytest.mark.unit def test_edit_user_patch_bad_expire_date(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = {"user": {"first_name": "user"}, "expire_date": date.today() - timedelta(days=1)} response = client.patch(url, data=data, content_type="application/json", format="json") assert response.status_code == 400 @pytest.mark.unit def test_edit_user_patch_today_expire_date(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = {"user": {"first_name": "user"}, "expire_date": date.today()} response = client.patch(url, data=data, content_type="application/json", format="json") assert response.status_code == 400 @pytest.mark.unit def test_edit_user_without_user(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = {"comment": "asdasdasd"} response = client.patch(url, data=data, content_type="application/json", format="json") edited_customer = User.objects.get(pk=customer.pk) info = UserInfo.objects.get(user=edited_customer) assert response.status_code == 400 @pytest.mark.unit def test_edit_user_patch_with_password(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = { "user": {"username": "Max", "first_name": "Ivan", "is_active": False, "password": "IamAlien2"}, "comment": "comment for new user", "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json") edited_customer = User.objects.get(pk=customer.pk) response_ = client.post(reverse('api_login'), {"username": "Max", "password": "IamAlien2"}) assert response_.wsgi_request.user.is_authenticated == True assert edited_customer.username == 'Max' assert response.status_code == 200 assert edited_customer.first_name == "Ivan" assert not edited_customer.is_active client.logout() @pytest.mark.unit def test_edit_user_patch_with_bad_password(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = { "user": {"username": "Max", "first_name": "Ivan", "is_active": False, "password": "12345675"}, "comment": "comment for new user", "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json", format="json") assert response.status_code == 400 @pytest.mark.unit def test_edit_user_patch_with_short_password(self, client, add_user_with_permissions, create_user_info): customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) client.force_login(self.user) data = { "user": {"username": "Max", "first_name": "Ivan", "is_active": False, "password": "Thepss1"}, "comment": "comment for new user", "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json", format="json") assert response.status_code == 400 @pytest.mark.unit def test_edit_user_patch_with_previous_password(self, client, add_user_with_permissions): user = add_user_with_permissions(username='user0', password='User1user!') url = reverse('active-users-detail', kwargs={"pk": user.pk}) client.force_login(self.user) data = {"user": {"password": "User1user!"}} response = client.patch(url, data=data, content_type="application/json", format="json") assert response.status_code == 400 @pytest.mark.unit def test_edit_user_with_too_long_username(self, api_client, add_user_with_permissions): user = add_user_with_permissions(username='user0', password='User1user!') url = reverse('active-users-detail', kwargs={"pk": user.pk}) api_client.force_authenticate(self.user) long_username = 'a' * 150 data = {"user": {"username": long_username}} response = api_client.patch(url, data=data, format="json") assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()['user']['username'] == ["Ensure this field has no more than 131 characters."] normal_username = 'a' * 131 data = {"user": {"username": normal_username}, 'timezone': 'Africa/Bangui'} response = api_client.patch(url, data=data, format="json") print(response.json()) assert response.status_code == status.HTTP_200_OK @pytest.mark.unit def test_delete_user(self, client, add_user_with_permissions, create_user_info): operator = User.objects.get(username="Max") client.force_login(self.user) url = reverse('active-users-detail', kwargs={"pk": operator.pk}) response = client.delete(url) assert response.status_code == 204 with pytest.raises(ObjectDoesNotExist): User.objects.get(username="Max") assert False @pytest.mark.unit def test_user_cannot_delete_himself(self, api_client): api_client.force_authenticate(self.user) url = reverse('active-users-detail', kwargs={"pk": self.user.pk}) response = api_client.delete(url) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == {'details': 'You cannot delete your account'} @pytest.mark.unit def test_admin_disable_himself(self, client, add_user_with_permissions, create_user_info): admin = add_user_with_permissions(username='user007', password='passwd12', is_superuser=True) url = reverse('active-users-detail', kwargs={"pk": admin.userinfo.pk}) client.force_login(admin) data = {"user": {"is_active": False}} response = client.patch(url, data=data, content_type="application/json", format="json") edited_admin = User.objects.get(pk=admin.pk) assert response.status_code == 400 assert edited_admin.is_active @pytest.mark.unit def test_admin_disable_other(self, client, add_user_with_permissions, create_user_info): admin = add_user_with_permissions(username='user007', password='passwd12', is_superuser=True) url = reverse('active-users-detail', kwargs={"pk": admin.userinfo.pk}) client.force_login(self.user) data = {"user": {"is_active": False}, "timezone": "Europe/Moscow"} response = client.patch(url, data=data, content_type="application/json", format="json") edited_admin = User.objects.get(pk=admin.pk) assert response.status_code == 200 assert not edited_admin.is_active @pytest.mark.unit def test_user_whoami(self, client, add_user_with_permissions, create_user_info): url = reverse('active-users-whoami') client.force_login(self.user) response = client.get(url) data_json = response.json() assert response.status_code == 200 assert data_json["username"] == "test_username1" def test_validate_username_create_user(self, client): """Test create user with bad username(with space and special symbol).""" url = reverse('active-users-list') client.force_login(self.user) users_before = User.objects.count() data = { "user": { "username": "username with space and +-#", "first_name": "swagger_name", "is_active": True, "email": "swagger@mail.ru", "password": "Pas123wordZ" }, "comment": "asdasd", "timezone": "Europe/Moscow", "expire_date": "2030-10-10" } response = client.post(url, data=json.dumps(data), content_type="application/json") assert response.status_code == 400 assert response.json() == {'user': ['The username must be contain only latin alphabet and digit, 0-9']} @pytest.mark.unit def test_edit_user_password(self, api_client, add_user_with_permissions, create_user_info): api_client.force_authenticate(self.user) customer = User.objects.get(username='Max') url = reverse('active-users-detail', kwargs={"pk": customer.pk}) data = { "user": {"username": "Max", "first_name": "Ivan", "is_active": False, "password": "IamAlien2"}, "comment": "comment for new user", "timezone": "Europe/Moscow"} response = api_client.patch(url, data=data, format='json') assert response.status_code == status.HTTP_200_OK @pytest.mark.unit def test_change_password_for_myself(self, api_client, add_user_with_permissions, create_user_info): api_client.force_authenticate(self.user) url = reverse('active-users-detail', kwargs={"pk": self.user.pk}) data = { "user": {"username": "TESTTEST", "first_name": "Ivan", "password": "IamAlien2"}, "comment": "comment for new user", "timezone": "Europe/Moscow"} response = api_client.patch(url, data=data, format='json') assert response.status_code == status.HTTP_400_BAD_REQUEST # because old_password is required data['user']['old_password'] = 'testpwd' response = api_client.patch(url, data=data, format='json') assert response.status_code == status.HTTP_200_OK @pytest.mark.django_db class TestUserExpire(object): @pytest.mark.unit def test_expiry(self): """ Tests that user becomes disabled if their expire date has happened """ user_before_count = User.objects.filter(is_active=True).count() user = User.objects.create_user(username='expired_active', is_active=True) UserInfo(user=user, expire_date=dtnow(days=-3).date()).save() user = User.objects.create_user(username='expired_not_active', is_active=False) UserInfo(user=user, expire_date=dtnow(days=-1).date()).save() user = User.objects.create_user(username='not_expired_active', is_active=True) UserInfo(user=user, expire_date=dtnow(days=1).date()).save() user = User.objects.create_user(username='not_expired_not_active', is_active=False) UserInfo(user=user, expire_date=dtnow(days=3).date()).save() user = User.objects.create_user(username='no_expire_active', is_active=True) UserInfo(user=user).save() user = User.objects.create_user(username='no_expire_not_active', is_active=False) UserInfo(user=user).save() assert User.objects.filter(is_active=True).count() == 3 + user_before_count expire_users() assert User.objects.filter(is_active=True).count() == 2 + user_before_count @pytest.mark.django_db class TestPasswordValidator: @pytest.mark.uint def test_password_validator(self): passwords = [{'name': 'Short password', 'value': 'asdW567', 'expect': 'error', 'result': 'Not test'}, {'name': 'Only digit', 'value': '12345678', 'expect': 'error', 'result': 'Not test'}, {'name': 'No upper-case letter', 'value': '123qwert', 'expect': 'error', 'result': 'Not test'}, {'name': 'No lower-case letter', 'value': '123QWERT', 'expect': 'error', 'result': 'Not test'}, {'name': 'Good password', 'value': '123qweWW', 'expect': 'success', 'result': 'Not test'}] test_result = True for p in passwords: try: validate_password(p['value'], user=None) if p['expect'] == 'error': test_result = False p['result'] = 'Bad' else: p['result'] = 'Ok' except ValidationError: if p['expect'] == 'success': test_result = False p['result'] = 'Bad' else: p['result'] = 'Ok' _log.info(f"Test {p['name']}, expect {p['expect']}, result - {p['result']}") assert test_result == True def get_params(step): params = ( (dtnow() - datetime.timedelta(minutes=2), 'offline'), (dtnow(), 'online'), (None, 'offline'), ) return params[step] @pytest.mark.django_db @pytest.mark.unit class TestUserStatusService: @pytest.mark.parametrize('step', range(3)) def test_user_get_status_online_offline(self, step, add_user_with_permissions): user = add_user_with_permissions(username='test_username_status', password='Testpwd!123*', is_superuser=True) user.userinfo.last_seen = get_params(step)[0] user.userinfo.save() service = UserStatusService(user, strategy=InUserInfoStrategy) assert service.get_status() == get_params(step)[1]