diff options
Diffstat (limited to 'alphabetlearning/users/tests')
-rw-r--r-- | alphabetlearning/users/tests/__init__.py | 0 | ||||
-rw-r--r-- | alphabetlearning/users/tests/factories.py | 40 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_admin.py | 65 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_forms.py | 35 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_managers.py | 55 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_models.py | 5 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_tasks.py | 17 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_urls.py | 19 | ||||
-rw-r--r-- | alphabetlearning/users/tests/test_views.py | 101 |
9 files changed, 337 insertions, 0 deletions
diff --git a/alphabetlearning/users/tests/__init__.py b/alphabetlearning/users/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/alphabetlearning/users/tests/__init__.py diff --git a/alphabetlearning/users/tests/factories.py b/alphabetlearning/users/tests/factories.py new file mode 100644 index 0000000..c0e94a3 --- /dev/null +++ b/alphabetlearning/users/tests/factories.py @@ -0,0 +1,40 @@ +from collections.abc import Sequence +from typing import Any + +from factory import Faker +from factory import post_generation +from factory.django import DjangoModelFactory + +from alphabetlearning.users.models import User + + +class UserFactory(DjangoModelFactory): + email = Faker("email") + name = Faker("name") + + @post_generation + def password(self, create: bool, extracted: Sequence[Any], **kwargs): # noqa: FBT001 + password = ( + extracted + if extracted + else Faker( + "password", + length=42, + special_chars=True, + digits=True, + upper_case=True, + lower_case=True, + ).evaluate(None, None, extra={"locale": None}) + ) + self.set_password(password) + + @classmethod + def _after_postgeneration(cls, instance, create, results=None): + """Save again the instance if creating and at least one hook ran.""" + if create and results and not cls._meta.skip_postgeneration_save: + # Some post-generation hooks ran, and may have modified us. + instance.save() + + class Meta: + model = User + django_get_or_create = ["email"] diff --git a/alphabetlearning/users/tests/test_admin.py b/alphabetlearning/users/tests/test_admin.py new file mode 100644 index 0000000..bd7296f --- /dev/null +++ b/alphabetlearning/users/tests/test_admin.py @@ -0,0 +1,65 @@ +import contextlib +from http import HTTPStatus +from importlib import reload + +import pytest +from django.contrib import admin +from django.contrib.auth.models import AnonymousUser +from django.urls import reverse +from pytest_django.asserts import assertRedirects + +from alphabetlearning.users.models import User + + +class TestUserAdmin: + def test_changelist(self, admin_client): + url = reverse("admin:users_user_changelist") + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + def test_search(self, admin_client): + url = reverse("admin:users_user_changelist") + response = admin_client.get(url, data={"q": "test"}) + assert response.status_code == HTTPStatus.OK + + def test_add(self, admin_client): + url = reverse("admin:users_user_add") + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + response = admin_client.post( + url, + data={ + "email": "new-admin@example.com", + "password1": "My_R@ndom-P@ssw0rd", + "password2": "My_R@ndom-P@ssw0rd", + }, + ) + assert response.status_code == HTTPStatus.FOUND + assert User.objects.filter(email="new-admin@example.com").exists() + + def test_view_user(self, admin_client): + user = User.objects.get(email="admin@example.com") + url = reverse("admin:users_user_change", kwargs={"object_id": user.pk}) + response = admin_client.get(url) + assert response.status_code == HTTPStatus.OK + + @pytest.fixture() + def _force_allauth(self, settings): + settings.DJANGO_ADMIN_FORCE_ALLAUTH = True + # Reload the admin module to apply the setting change + import alphabetlearning.users.admin as users_admin + + with contextlib.suppress(admin.sites.AlreadyRegistered): + reload(users_admin) + + @pytest.mark.django_db() + @pytest.mark.usefixtures("_force_allauth") + def test_allauth_login(self, rf, settings): + request = rf.get("/fake-url") + request.user = AnonymousUser() + response = admin.site.login(request) + + # The `admin` login view should redirect to the `allauth` login view + target_url = reverse(settings.LOGIN_URL) + "?next=" + request.path + assertRedirects(response, target_url, fetch_redirect_response=False) diff --git a/alphabetlearning/users/tests/test_forms.py b/alphabetlearning/users/tests/test_forms.py new file mode 100644 index 0000000..9ab1f7e --- /dev/null +++ b/alphabetlearning/users/tests/test_forms.py @@ -0,0 +1,35 @@ +"""Module for all Form Tests.""" + +from django.utils.translation import gettext_lazy as _ + +from alphabetlearning.users.forms import UserAdminCreationForm +from alphabetlearning.users.models import User + + +class TestUserAdminCreationForm: + """ + Test class for all tests related to the UserAdminCreationForm + """ + + def test_username_validation_error_msg(self, user: User): + """ + Tests UserAdminCreation Form's unique validator functions correctly by testing: + 1) A new user with an existing username cannot be added. + 2) Only 1 error is raised by the UserCreation Form + 3) The desired error message is raised + """ + + # The user already exists, + # hence cannot be created. + form = UserAdminCreationForm( + { + "email": user.email, + "password1": user.password, + "password2": user.password, + }, + ) + + assert not form.is_valid() + assert len(form.errors) == 1 + assert "email" in form.errors + assert form.errors["email"][0] == _("This email has already been taken.") diff --git a/alphabetlearning/users/tests/test_managers.py b/alphabetlearning/users/tests/test_managers.py new file mode 100644 index 0000000..1e5a5e3 --- /dev/null +++ b/alphabetlearning/users/tests/test_managers.py @@ -0,0 +1,55 @@ +from io import StringIO + +import pytest +from django.core.management import call_command + +from alphabetlearning.users.models import User + + +@pytest.mark.django_db() +class TestUserManager: + def test_create_user(self): + user = User.objects.create_user( + email="john@example.com", + password="something-r@nd0m!", # noqa: S106 + ) + assert user.email == "john@example.com" + assert not user.is_staff + assert not user.is_superuser + assert user.check_password("something-r@nd0m!") + assert user.username is None + + def test_create_superuser(self): + user = User.objects.create_superuser( + email="admin@example.com", + password="something-r@nd0m!", # noqa: S106 + ) + assert user.email == "admin@example.com" + assert user.is_staff + assert user.is_superuser + assert user.username is None + + def test_create_superuser_username_ignored(self): + user = User.objects.create_superuser( + email="test@example.com", + password="something-r@nd0m!", # noqa: S106 + ) + assert user.username is None + + +@pytest.mark.django_db() +def test_createsuperuser_command(): + """Ensure createsuperuser command works with our custom manager.""" + out = StringIO() + command_result = call_command( + "createsuperuser", + "--email", + "henry@example.com", + interactive=False, + stdout=out, + ) + + assert command_result is None + assert out.getvalue() == "Superuser created successfully.\n" + user = User.objects.get(email="henry@example.com") + assert not user.has_usable_password() diff --git a/alphabetlearning/users/tests/test_models.py b/alphabetlearning/users/tests/test_models.py new file mode 100644 index 0000000..502536f --- /dev/null +++ b/alphabetlearning/users/tests/test_models.py @@ -0,0 +1,5 @@ +from alphabetlearning.users.models import User + + +def test_user_get_absolute_url(user: User): + assert user.get_absolute_url() == f"/users/{user.pk}/" diff --git a/alphabetlearning/users/tests/test_tasks.py b/alphabetlearning/users/tests/test_tasks.py new file mode 100644 index 0000000..92f0e8a --- /dev/null +++ b/alphabetlearning/users/tests/test_tasks.py @@ -0,0 +1,17 @@ +import pytest +from celery.result import EagerResult + +from alphabetlearning.users.tasks import get_users_count +from alphabetlearning.users.tests.factories import UserFactory + +pytestmark = pytest.mark.django_db + + +def test_user_count(settings): + """A basic test to execute the get_users_count Celery task.""" + batch_size = 3 + UserFactory.create_batch(batch_size) + settings.CELERY_TASK_ALWAYS_EAGER = True + task_result = get_users_count.delay() + assert isinstance(task_result, EagerResult) + assert task_result.result == batch_size diff --git a/alphabetlearning/users/tests/test_urls.py b/alphabetlearning/users/tests/test_urls.py new file mode 100644 index 0000000..e70dc03 --- /dev/null +++ b/alphabetlearning/users/tests/test_urls.py @@ -0,0 +1,19 @@ +from django.urls import resolve +from django.urls import reverse + +from alphabetlearning.users.models import User + + +def test_detail(user: User): + assert reverse("users:detail", kwargs={"pk": user.pk}) == f"/users/{user.pk}/" + assert resolve(f"/users/{user.pk}/").view_name == "users:detail" + + +def test_update(): + assert reverse("users:update") == "/users/~update/" + assert resolve("/users/~update/").view_name == "users:update" + + +def test_redirect(): + assert reverse("users:redirect") == "/users/~redirect/" + assert resolve("/users/~redirect/").view_name == "users:redirect" diff --git a/alphabetlearning/users/tests/test_views.py b/alphabetlearning/users/tests/test_views.py new file mode 100644 index 0000000..27e6064 --- /dev/null +++ b/alphabetlearning/users/tests/test_views.py @@ -0,0 +1,101 @@ +from http import HTTPStatus + +import pytest +from django.conf import settings +from django.contrib import messages +from django.contrib.auth.models import AnonymousUser +from django.contrib.messages.middleware import MessageMiddleware +from django.contrib.sessions.middleware import SessionMiddleware +from django.http import HttpRequest +from django.http import HttpResponseRedirect +from django.test import RequestFactory +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from alphabetlearning.users.forms import UserAdminChangeForm +from alphabetlearning.users.models import User +from alphabetlearning.users.tests.factories import UserFactory +from alphabetlearning.users.views import UserRedirectView +from alphabetlearning.users.views import UserUpdateView +from alphabetlearning.users.views import user_detail_view + +pytestmark = pytest.mark.django_db + + +class TestUserUpdateView: + """ + TODO: + extracting view initialization code as class-scoped fixture + would be great if only pytest-django supported non-function-scoped + fixture db access -- this is a work-in-progress for now: + https://github.com/pytest-dev/pytest-django/pull/258 + """ + + def dummy_get_response(self, request: HttpRequest): + return None + + def test_get_success_url(self, user: User, rf: RequestFactory): + view = UserUpdateView() + request = rf.get("/fake-url/") + request.user = user + + view.request = request + assert view.get_success_url() == f"/users/{user.pk}/" + + def test_get_object(self, user: User, rf: RequestFactory): + view = UserUpdateView() + request = rf.get("/fake-url/") + request.user = user + + view.request = request + + assert view.get_object() == user + + def test_form_valid(self, user: User, rf: RequestFactory): + view = UserUpdateView() + request = rf.get("/fake-url/") + + # Add the session/message middleware to the request + SessionMiddleware(self.dummy_get_response).process_request(request) + MessageMiddleware(self.dummy_get_response).process_request(request) + request.user = user + + view.request = request + + # Initialize the form + form = UserAdminChangeForm() + form.cleaned_data = {} + form.instance = user + view.form_valid(form) + + messages_sent = [m.message for m in messages.get_messages(request)] + assert messages_sent == [_("Information successfully updated")] + + +class TestUserRedirectView: + def test_get_redirect_url(self, user: User, rf: RequestFactory): + view = UserRedirectView() + request = rf.get("/fake-url") + request.user = user + + view.request = request + assert view.get_redirect_url() == f"/users/{user.pk}/" + + +class TestUserDetailView: + def test_authenticated(self, user: User, rf: RequestFactory): + request = rf.get("/fake-url/") + request.user = UserFactory() + response = user_detail_view(request, pk=user.pk) + + assert response.status_code == HTTPStatus.OK + + def test_not_authenticated(self, user: User, rf: RequestFactory): + request = rf.get("/fake-url/") + request.user = AnonymousUser() + response = user_detail_view(request, pk=user.pk) + login_url = reverse(settings.LOGIN_URL) + + assert isinstance(response, HttpResponseRedirect) + assert response.status_code == HTTPStatus.FOUND + assert response.url == f"{login_url}?next=/fake-url/" |