Skip to content
This repository has been archived by the owner on Mar 19, 2024. It is now read-only.

Add tests #77

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 163 additions & 101 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ structlog = "^23.1"
requests = "^2.28"
attrs = "^23.1"
pydantic = "^2.3"
punq = "^0.6"
punq = "^0.6.2"

[tool.poetry.group.dev.dependencies]
django-debug-toolbar = "^4.2"
django-querycount = "^0.8"
django-migration-linter = "^5.0"
django-extra-checks = "^0.13"
nplusone = "^1.0"
mimesis = "^11.1.0"

wemake-python-styleguide = "^0.18"
flake8-pytest-style = "^1.7"
Expand Down Expand Up @@ -66,6 +67,7 @@ dennis = "^1.1"
dump-env = "^1.3"
ipython = "^8.15"
import-linter = "^1.11"
faker = "^19.6.1"

[tool.poetry.group.docs]
optional = true
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ DJANGO_SETTINGS_MODULE = server.settings
# You should adjust this value to be as low as possible.
# Configuration:
# https://pypi.org/project/pytest-timeout/
timeout = 5
timeout = 2

# Strict `@xfail` by default:
xfail_strict = true
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@
pytest_plugins = [
# Should be the first custom one:
'plugins.django_settings',

# TODO: add your own plugins here!
'plugins.identity.user',
]
Empty file added tests/plugins/__init__.py
Empty file.
105 changes: 105 additions & 0 deletions tests/plugins/identity/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import datetime as dt
from typing import Callable, Protocol, TypeAlias, TypedDict, Unpack, final

import pytest
from mimesis import Field, Locale, Schema

from server.apps.identity.models import User


class UserData(TypedDict, total=False):
"""
Represent the simplified user data that is required to create a new user.

It does not include ``password``, because it is very special in django.
"""

email: str
first_name: str
last_name: str
date_of_birth: dt.datetime
address: str
job_title: str
phone: str


@final
class RegistrationData(UserData, total=False):
"""Represent the registration data that is required to create a new user."""

password1: str
password2: str


@final
class RegistrationDataFactory(Protocol):
"""User data factory protocol."""

def __call__(self, **fields: Unpack[RegistrationData]) -> RegistrationData:
"""Method to call as a function with RegistrationData as kwargs."""


UserAssertion: TypeAlias = Callable[[str, UserData], None]


@pytest.fixture()
def registration_data_factory(faker_seed: int) -> RegistrationDataFactory:
"""Returns factory for random data generation."""
def factory(**fields: Unpack[RegistrationData]) -> RegistrationData:
mf = Field(locale=Locale.RU, seed=faker_seed)
password = mf('password')
schema = Schema(
schema=lambda: {
'email': mf('person:email'),
'first_name': mf('person:first_name'),
'last_name': mf('person:last_name'),
'date_of_birth': mf('datetime:date'),
'address': mf('address.city'),
'job_title': mf('person.occupation'),
'phone': mf('person.telephone'),
}, iterations=1,
)
return {
**schema.create()[0], # type: ignore[misc]
**{'password1': password, 'password2': password},
**fields,
}

return factory


@pytest.fixture()
def registration_data(
registration_data_factory: RegistrationDataFactory,
) -> RegistrationData:
"""Returns fake random data for regitration."""
return registration_data_factory()


@pytest.fixture()
def user_data(registration_data: RegistrationData) -> UserData:
"""
We need to simplify registration data to drop passwords.

Basically, it is the same as ``registration_data``, but without passwords.
"""
return { # type: ignore[return-value]
key_name: value_part
for key_name, value_part in registration_data.items()
if not key_name.startswith('password')
}


@pytest.fixture(scope='session')
def assert_correct_user() -> UserAssertion:
"""Factory to compare all user fields."""
def factory(email: str, expected: UserData) -> None:
user = User.objects.get(email=email)
assert user.id
assert user.is_active
assert not user.is_superuser
assert not user.is_staff
for field_name, data_value in expected.items():
assert getattr(user, field_name) == data_value

return factory
20 changes: 20 additions & 0 deletions tests/test_apps/test_identity/test_registration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from http import HTTPStatus

import pytest
from django.test.client import Client
from plugins.identity.user import RegistrationData, UserAssertion, UserData


@pytest.mark.django_db()
def test_valid_registration(
client: Client,
registration_data: RegistrationData,
user_data: UserData,
assert_correct_user: UserAssertion,
) -> None:
"""Test that registration works with correct user data."""
response = client.post('/identity/registration', data=registration_data)

assert response.status_code == HTTPStatus.FOUND
assert response.get('Location') == '/identity/login'
assert_correct_user(registration_data['email'], user_data)
Loading