This repository has been archived by the owner on Mar 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 111
Home work-1 a_petrushkin #65
Closed
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
2c3757e
HW-01 add dev dependency
a676348
Merge pull request #1 from Hazzari/HW-01_dev
Hazzari 6fe0a38
fix deprecated method
488e060
fix error in directory name
8c56e4c
Merge pull request #2 from Hazzari/boalerplate_fix
Hazzari 2b000a7
fix spelling error in variable
b43425a
Merge pull request #3 from Hazzari/HW-01_dev
Hazzari 9ac0c00
Turn off errors from base repository
f9fd700
add dev dependency
99afbd4
disable unnecessary warnings in libraries
e3652aa
Update dev dependency
828b887
Add ruff rules
356bbf6
Add homework tests
d81cc9e
Merge branch 'master' into hw-1
d713209
Merge pull request #4 from Hazzari/hw-1
Hazzari 8878dc5
Merge remote-tracking branch 'origin/master'
790d856
Add test for user manager, causes an error if you pass an empty email…
c623818
Merge remote-tracking branch 'upstream/master'
44276ad
Fix name variables
2e15615
Add tests
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,4 +11,5 @@ | |
'plugins.django_settings', | ||
|
||
# TODO: add your own plugins here! | ||
'plugins.identity.user', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import datetime as dt | ||
from collections.abc import Callable | ||
from random import SystemRandom | ||
from typing import Protocol, TypeAlias, TypedDict, Unpack, cast, final | ||
|
||
import pytest | ||
from django.utils.crypto import RANDOM_STRING_CHARS, get_random_string | ||
from mimesis import BaseProvider, Field, Locale | ||
from mimesis.schema import Schema | ||
|
||
from server.apps.identity.models import User | ||
|
||
UserAssertion: TypeAlias = Callable[[str, 'UserData'], None] | ||
|
||
min_len, max_len = 10, 20 | ||
|
||
|
||
class FakeProvider(BaseProvider): | ||
"""Represents a fake test data provider.""" | ||
|
||
class Meta: # noqa: WPS306 | ||
name = 'FakeProvider' | ||
|
||
def random_seed(self) -> int: | ||
"""Generate random seed.""" | ||
return self.random.randint(0, 100) | ||
|
||
def user_factory( | ||
self, | ||
**fields: Unpack['RegistrationData'], | ||
) -> 'RegistrationData': | ||
"""Factory for generating user data used during registration.""" | ||
mf = Field(locale=Locale.RU, seed=self.random_seed()) | ||
password = mf('password') # by default passwords are equal | ||
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'), | ||
}, | ||
) | ||
return { | ||
**schema.create()[0], # type: ignore[typeddict-item] | ||
**{'password1': password, 'password2': password}, | ||
**fields, | ||
} | ||
|
||
def random_string( | ||
self, | ||
min_default_len: int = min_len, | ||
max_default_len: int = max_len, | ||
) -> str: | ||
"""Create a random string.""" | ||
return get_random_string( | ||
length=SystemRandom().randrange( | ||
start=min_default_len, | ||
stop=max_default_len, | ||
), | ||
allowed_chars=RANDOM_STRING_CHARS, | ||
) | ||
|
||
|
||
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. | ||
Importing this type is only allowed under ``if TYPE_CHECKING`` in tests. | ||
""" | ||
|
||
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. | ||
|
||
Importing this type is only allowed under ``if TYPE_CHECKING`` in tests. | ||
""" | ||
|
||
password1: str | ||
password2: str | ||
|
||
|
||
class RegistrationDataFactory(Protocol): | ||
"""User data factory protocol.""" | ||
|
||
def __call__(self, **fields: Unpack[RegistrationData]) -> RegistrationData: | ||
"""Must implement in logic.""" | ||
|
||
|
||
@pytest.fixture() | ||
def expected_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 cast( | ||
UserData, | ||
{ | ||
key_name: value_part | ||
for key_name, value_part in registration_data.items() | ||
if not key_name.startswith('password') | ||
}, | ||
) | ||
|
||
|
||
@pytest.fixture() | ||
def registration_data( | ||
registration_data_factory: RegistrationDataFactory, | ||
) -> RegistrationData: | ||
"""Returns fake random data for registration.""" | ||
return registration_data_factory() | ||
|
||
|
||
@pytest.fixture() | ||
def registration_data_factory() -> RegistrationDataFactory: | ||
"""Returns factory for fake random data for registration.""" | ||
return FakeProvider().user_factory | ||
|
||
|
||
@pytest.fixture() | ||
def create_user(expected_user_data: UserData) -> User: | ||
"""Create a user.""" | ||
return User.objects.create(**expected_user_data) | ||
|
||
|
||
@pytest.fixture(scope='session') | ||
def assert_correct_user() -> UserAssertion: | ||
"""Asserts that user with the given email exists and has expected data.""" | ||
|
||
def factory(email: str, expected: UserData) -> None: | ||
user = User.objects.get(email=email) | ||
# Special fields: | ||
assert user.id | ||
assert user.is_active | ||
assert not user.is_superuser | ||
assert not user.is_staff | ||
# All other fields: | ||
for field_name, data_value in expected.items(): | ||
assert getattr(user, field_name) == data_value | ||
|
||
return factory | ||
|
||
|
||
@pytest.fixture() | ||
def random_string() -> Callable[..., str]: | ||
"""Give a fixture that can generate a string of a given length.""" | ||
return FakeProvider().random_string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from http import HTTPStatus | ||
|
||
import pytest | ||
from django.test import Client | ||
from django.urls import reverse | ||
|
||
from server.apps.identity.models import User | ||
|
||
|
||
@pytest.mark.django_db() | ||
def test_valid_login( | ||
client: Client, | ||
create_user: User, | ||
) -> None: | ||
"""A valid user login must redirect to the home page.""" | ||
client.force_login(create_user) | ||
|
||
response = client.get(reverse('index')) | ||
|
||
assert response.status_code == HTTPStatus.OK | ||
assert 'Личный кабинет'.encode('utf-8') in response.content |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
from http import HTTPStatus | ||
from typing import TYPE_CHECKING | ||
|
||
import pytest | ||
from django.test import Client | ||
from django.urls import reverse | ||
|
||
from server.apps.identity.models import User | ||
|
||
if TYPE_CHECKING: | ||
from tests.plugins.identity.user import ( | ||
RegistrationData, | ||
RegistrationDataFactory, | ||
UserAssertion, | ||
UserData, | ||
) | ||
|
||
invalid_emails = [ | ||
'plain_address', # Простой адрес без @ | ||
'@missing_username.com', # Отсутствует имя пользователя перед @ | ||
'[email protected]', # Отсутствует доменное имя после @ | ||
'[email protected]', # Два точечных символа в домене | ||
'[email protected]', # Домен заканчивается недопустимым символом | ||
'[email protected]', # Домен начинается с точечного символа | ||
'[email protected]', # Имя пользователя начинается с точечного символа | ||
'username@domain,com', # Использует запятую вместо точки | ||
'username@', # Отсутствует доменное имя | ||
'username@[email protected]', # Содержит два символа @ | ||
'[email protected]', # Содержит два точечных символа | ||
# FIXME: domain.-com - дает ложно отрицательный результат в тесте | ||
# '[email protected]', # Домен начинается с недопустимого символа | ||
] | ||
|
||
|
||
@pytest.mark.django_db() | ||
def test_valid_registration( | ||
client: Client, | ||
registration_data: 'RegistrationData', | ||
expected_user_data: 'UserData', | ||
assert_correct_user: 'UserAssertion', | ||
) -> None: | ||
"""Test that registration works with correct user data.""" | ||
response = client.post( | ||
reverse('identity:registration'), | ||
data=registration_data, | ||
) | ||
assert response.status_code == HTTPStatus.FOUND | ||
assert response.get('Location') == reverse('identity:login') | ||
assert_correct_user(registration_data['email'], expected_user_data) | ||
|
||
|
||
@pytest.mark.django_db() | ||
def test_registration_missing_required_field( | ||
client: Client, | ||
registration_data_factory: 'RegistrationDataFactory', | ||
) -> None: | ||
"""Test that missing required will fail the registration.""" | ||
post_data = registration_data_factory(email='') | ||
response = client.post( | ||
reverse('identity:registration'), | ||
data=post_data, | ||
) | ||
assert response.status_code == HTTPStatus.OK | ||
assert not User.objects.filter(email=post_data['email']) | ||
|
||
|
||
@pytest.mark.parametrize('bad_email', invalid_emails) | ||
@pytest.mark.django_db() | ||
def test_bad_format_email_required_field( | ||
bad_email: str, | ||
client: Client, | ||
registration_data_factory: 'RegistrationDataFactory', | ||
) -> None: | ||
"""Test to check with different non-valid email formats.""" | ||
user_with_invalid_email = registration_data_factory(email=bad_email) | ||
response = client.post( | ||
reverse('identity:registration'), | ||
data=user_with_invalid_email, | ||
) | ||
assert response.status_code == HTTPStatus.OK | ||
assert not User.objects.filter(email=user_with_invalid_email['email']) | ||
|
||
|
||
@pytest.mark.django_db() | ||
def test_user_manager_create_error(random_string): | ||
"""If email is missing, error is called.""" | ||
random_user = { | ||
'email': None, | ||
'password': random_string(), | ||
'first_name': random_string(), | ||
'last_name': random_string(), | ||
'phone': random_string(), | ||
} | ||
with pytest.raises(ValueError, match='Users must have an email address'): | ||
User.objects.create_user(**random_user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from http import HTTPStatus | ||
from typing import TYPE_CHECKING | ||
|
||
import pytest | ||
from django.urls import reverse | ||
|
||
if TYPE_CHECKING: | ||
from django.test import Client | ||
|
||
from server.apps.identity.models import User | ||
|
||
|
||
@pytest.mark.django_db() | ||
def test_user_update_template( | ||
client: 'Client', | ||
create_user: 'User', | ||
) -> None: | ||
"""Test get template for update user.""" | ||
client.force_login(create_user) | ||
response = client.get(reverse('identity:user_update')) | ||
|
||
assert response.status_code == HTTPStatus.OK | ||
assert 'Редактировать профиль'.encode('utf-8') in response.content |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import pytest | ||
|
||
from server.apps.pictures.models import FavouritePicture | ||
|
||
favourite_picture_params = [ | ||
(1234567890, 'some_test_foreign_id'), | ||
(1234567890, None), | ||
(None, 'some_test_foreign_id'), | ||
(None, None), | ||
] | ||
|
||
|
||
@pytest.mark.parametrize(('foreign_id', 'user_id'), favourite_picture_params) | ||
@pytest.mark.django_db() | ||
def test_output_correct_str(foreign_id, user_id): | ||
"""Test that string representation is correct.""" | ||
string_format = FavouritePicture(foreign_id=foreign_id, user_id=user_id) | ||
|
||
assert str(string_format) == f'<Picture {foreign_id} by {user_id}>' |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Константы лучше в верхнем регистре сделать, и подобрать имя более конкретное, типа MIN_STRING_LENGTH