-
Notifications
You must be signed in to change notification settings - Fork 109
Lesson1 updates #68
base: master
Are you sure you want to change the base?
Lesson1 updates #68
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| """Identity data related plugins.""" | ||
| from datetime import datetime | ||
| from typing import Callable, Protocol, TypeAlias, TypedDict, Unpack, final | ||
|
|
||
| import pytest | ||
| from django.contrib.auth import get_user_model | ||
| from django.test import Client | ||
| from django.contrib.auth.models import User | ||
| from mimesis.locales import Locale | ||
| from mimesis.schema import Field, Schema | ||
|
|
||
|
|
||
| @final | ||
| class ProfileData(TypedDict, total=False): | ||
| """Represent the simplified profile data.""" | ||
|
|
||
| first_name: str | ||
| last_name: str | ||
| date_of_birth: datetime | ||
| address: str | ||
| job_title: str | ||
| phone: str | ||
|
|
||
|
|
||
| ProfileAssertion: TypeAlias = Callable[[str, ProfileData], None] | ||
|
|
||
|
|
||
| class ProfileDataFactory(Protocol): | ||
| """Factory for representation of the simplified profile data.""" | ||
|
|
||
| def __call__(self, **fields: Unpack[ProfileData]) -> ProfileData: | ||
| """User data factory protocol.""" | ||
|
|
||
|
|
||
| @pytest.fixture() | ||
| def profile_data_factory( | ||
| faker_seed: int, | ||
| ) -> ProfileDataFactory: | ||
| """Returns factory for fake random profile data.""" | ||
|
|
||
| def factory(**fields: Unpack[ProfileData]) -> ProfileData: | ||
| mf = Field(locale=Locale.EN, seed=faker_seed) | ||
| schema = Schema( | ||
| schema=lambda: { | ||
| '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[typeddict-item] | ||
| **fields, | ||
| } | ||
|
|
||
| return factory | ||
|
|
||
|
|
||
| @pytest.fixture(scope='session') | ||
| def assert_correct_profile() -> ProfileAssertion: | ||
| """All profile fields are equal to reference.""" | ||
|
|
||
| def factory(email: str, expected: ProfileData) -> None: | ||
| user = get_user_model().objects.get(email=email) | ||
| assert user.id | ||
| assert user.is_active | ||
| for field_name, data_value in expected.items(): | ||
| assert getattr(user, field_name) == data_value | ||
| return factory | ||
|
|
||
|
|
||
| @pytest.fixture(scope='session') | ||
| def assert_incorrect_profile() -> ProfileAssertion: | ||
| """At least one field does not match.""" | ||
|
|
||
| def factory(email: str, expected: ProfileData) -> None: | ||
| user = get_user_model().objects.get(email=email) | ||
| assert user.id | ||
| assert user.is_active | ||
| matches = [] | ||
| for field_name, data_value in expected.items(): | ||
| matches.append(getattr(user, field_name) == data_value) | ||
|
|
||
| assert not all(matches) | ||
|
|
||
| return factory | ||
|
|
||
|
|
||
| @pytest.fixture(scope='function') | ||
| def logged_user_client(client: Client, django_user_model: User): | ||
| """Client for a logged in user.""" | ||
| password, email = 'password', '[email protected]' | ||
| user = django_user_model.objects.create_user( | ||
| email, | ||
| password, | ||
| ) | ||
| client.force_login(user) | ||
| return client |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,11 @@ | |
|
|
||
| import pytest | ||
| from django.test import Client | ||
| from plugins.identity.user import ( | ||
| ProfileAssertion, | ||
| ProfileDataFactory, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: If we treat plugins as standalone modules, will this affect test or CI duration? As @sobolevn suggested, we could remove If plugins become significantly large, will this change influence the startup time or CI duration?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understand, I'm not really sure about |
||
| logged_user_client | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.django_db()() | ||
|
|
@@ -43,6 +48,57 @@ def test_admin_docs_authorized(admin_client: Client) -> None: | |
| assert b'docutils' not in response.content | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("url_found", [ | ||
| '/pictures/dashboard', | ||
| '/pictures/favourites' | ||
| ]) | ||
| def test_picture_pages_unauthorized(client: Client, url_found: str) -> None: | ||
| """This test ensures that picture management pages require auth.""" | ||
| response = client.get(url_found) | ||
| assert response.status_code == HTTPStatus.FOUND | ||
|
|
||
|
|
||
| @pytest.mark.django_db() | ||
| @pytest.mark.parametrize("url_accessible", [ | ||
| '/pictures/dashboard', | ||
| '/pictures/favourites' | ||
| ]) | ||
| def test_picture_pages_authorized( | ||
| logged_user_client: Client, | ||
| url_accessible: str | ||
| ) -> None: | ||
| """Ensures picture management pages are accessible for authorized user.""" | ||
|
|
||
| response = logged_user_client.get(url_accessible) | ||
| assert response.status_code == HTTPStatus.OK | ||
|
|
||
|
|
||
| @pytest.mark.django_db() | ||
| def test_profile_update_authorized( | ||
| logged_user_client: Client, | ||
| profile_data_factory: 'ProfileDataFactory', | ||
| assert_correct_profile: 'ProfileAssertion', | ||
| assert_incorrect_profile: 'ProfileAssertion', | ||
| ) -> None: | ||
| """This test ensures profile updating for an authorized user.""" | ||
| user_data = profile_data_factory() | ||
|
|
||
| # that is an email for `logged_user_client` fixture | ||
| # maybe add indirect parametrization? | ||
| email = '[email protected]' | ||
|
|
||
| # there might be a probability of accidental match, but disregard it for now | ||
| assert_incorrect_profile(email, user_data) | ||
|
|
||
| response = logged_user_client.post( | ||
| '/identity/update', | ||
| data=user_data, | ||
| ) | ||
| assert response.status_code == HTTPStatus.FOUND | ||
| assert response.get('Location') == '/identity/update' | ||
| assert_correct_profile(email, user_data) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize('page', [ | ||
| '/robots.txt', | ||
| '/humans.txt', | ||
|
|
||
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.
Минорный момент: лучше не продуктовые зависимости добавлять в
devгруппу.Например, это можно сделать командой
poetry add --group dev xxxили вручную переставить в файле.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.
оно вроде и есть в dev-зависимостях?
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.
Точно, проглядел.
My bad :(