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

Commit 108668f

Browse files
committed
Lesson1 updates
1 parent be3f28d commit 108668f

File tree

8 files changed

+270
-95
lines changed

8 files changed

+270
-95
lines changed

poetry.lock

Lines changed: 106 additions & 92 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ dennis = "^1.1"
6666
dump-env = "^1.3"
6767
ipython = "^8.15"
6868
import-linter = "^1.11"
69+
mimesis = "^11.1.0"
6970

7071
[tool.poetry.group.docs]
7172
optional = true

tests/conftest.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
"""
88

99
pytest_plugins = [
10-
# Should be the first custom one:
1110
'plugins.django_settings',
12-
13-
# TODO: add your own plugins here!
11+
'plugins.identity.user',
1412
]

tests/plugins/django_settings.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
from random import SystemRandom
2+
13
import pytest
24
from django.conf import LazySettings
35
from django.core.cache import BaseCache, caches
46

7+
MAX_RANDOM_INT = 999999
8+
59

610
@pytest.fixture(autouse=True)
711
def _media_root(
@@ -36,6 +40,12 @@ def _debug(settings: LazySettings) -> None:
3640
template['OPTIONS']['debug'] = True
3741

3842

43+
@pytest.fixture(scope='session', autouse=True)
44+
def faker_seed() -> int:
45+
"""Create a random seed for the Faker library."""
46+
return SystemRandom().randint(0, MAX_RANDOM_INT)
47+
48+
3949
@pytest.fixture(autouse=True)
4050
def cache(settings: LazySettings) -> BaseCache:
4151
"""Modifies how cache is used in Django tests."""

tests/plugins/identity/__init__.py

Whitespace-only changes.

tests/plugins/identity/user.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""Identity data related plugins."""
2+
from datetime import datetime
3+
from typing import Callable, Protocol, TypeAlias, TypedDict, Unpack, final
4+
5+
import pytest
6+
from django.contrib.auth import get_user_model
7+
from mimesis.locales import Locale
8+
from mimesis.schema import Field, Schema
9+
10+
11+
@final
12+
class ProfileData(TypedDict, total=False):
13+
"""Represent the simplified profile data."""
14+
15+
first_name: str
16+
last_name: str
17+
date_of_birth: datetime
18+
address: str
19+
job_title: str
20+
phone: str
21+
22+
23+
ProfileAssertion: TypeAlias = Callable[[str, ProfileData], None]
24+
25+
26+
@final
27+
class ProfileDataFactory(Protocol):
28+
"""Factory for representation of the simplified profile data."""
29+
30+
def __call__(
31+
self,
32+
**fields: Unpack[ProfileData],
33+
) -> ProfileData:
34+
"""User data factory protocol."""
35+
36+
37+
@pytest.fixture()
38+
def profile_data_factory(
39+
faker_seed: int,
40+
) -> ProfileDataFactory:
41+
"""Returns factory for fake random profile data."""
42+
43+
def factory(**fields: Unpack[ProfileData]) -> ProfileData:
44+
mf = Field(locale=Locale.EN, seed=faker_seed)
45+
schema = Schema(
46+
schema=lambda: {
47+
'first_name': mf('person.first_name'),
48+
'last_name': mf('person.last_name'),
49+
'date_of_birth': mf('datetime.date'),
50+
'address': mf('address.city'),
51+
'job_title': mf('person.occupation'),
52+
'phone': mf('person.telephone'),
53+
},
54+
iterations=1,
55+
)
56+
return {
57+
**schema.create()[0], # type: ignore[misc]
58+
**fields,
59+
}
60+
61+
return factory
62+
63+
64+
@pytest.fixture(scope='session')
65+
def assert_correct_profile() -> ProfileAssertion:
66+
"""All profile fields are equal to reference."""
67+
68+
def factory(email: str, expected: ProfileData) -> None:
69+
user = get_user_model().objects.get(email=email)
70+
assert user.id
71+
assert user.is_active
72+
for field_name, data_value in expected.items():
73+
assert getattr(user, field_name) == data_value
74+
return factory
75+
76+
77+
@pytest.fixture(scope='session')
78+
def assert_incorrect_profile() -> ProfileAssertion:
79+
"""At least one field does not match."""
80+
81+
def factory(email: str, expected: ProfileData) -> None:
82+
user = get_user_model().objects.get(email=email)
83+
assert user.id
84+
assert user.is_active
85+
matches = []
86+
for field_name, data_value in expected.items():
87+
matches.append(getattr(user, field_name) == data_value)
88+
89+
assert not all(matches)
90+
91+
return factory

tests/test_server/test_urls.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from http import HTTPStatus
22

33
import pytest
4+
from django.contrib.auth.models import User
45
from django.test import Client
6+
from plugins.identity.user import ProfileAssertion, ProfileDataFactory
57

68

79
@pytest.mark.django_db()()
@@ -43,6 +45,65 @@ def test_admin_docs_authorized(admin_client: Client) -> None:
4345
assert b'docutils' not in response.content
4446

4547

48+
def test_picture_pages_unauthorized(client: Client) -> None:
49+
"""This test ensures that picture management pages require auth."""
50+
response = client.get('/pictures/dashboard')
51+
assert response.status_code == HTTPStatus.FOUND
52+
53+
response = client.get('/pictures/favourites')
54+
assert response.status_code == HTTPStatus.FOUND
55+
56+
57+
@pytest.mark.django_db()
58+
def test_picture_pages_authorized(
59+
client: Client,
60+
django_user_model: User,
61+
) -> None:
62+
"""Ensures picture management pages are accessible for authorized user."""
63+
password, email = 'password', '[email protected]'
64+
user = django_user_model.objects.create_user(
65+
email,
66+
password,
67+
)
68+
client.force_login(user)
69+
70+
response = client.get('/pictures/dashboard')
71+
assert response.status_code == HTTPStatus.OK
72+
73+
response = client.get('/pictures/favourites')
74+
assert response.status_code == HTTPStatus.OK
75+
76+
77+
@pytest.mark.django_db()
78+
def test_profile_update_authorized(
79+
client: Client,
80+
django_user_model: User,
81+
profile_data_factory: 'ProfileDataFactory',
82+
assert_correct_profile: 'ProfileAssertion',
83+
assert_incorrect_profile: 'ProfileAssertion',
84+
) -> None:
85+
"""This test ensures profile updating for an authorized user."""
86+
user_data = profile_data_factory()
87+
88+
password, email = 'password', '[email protected]'
89+
user = django_user_model.objects.create_user(
90+
email,
91+
password,
92+
)
93+
client.force_login(user)
94+
95+
# there might be a probability of accidental match, but disregard it for now
96+
assert_incorrect_profile(email, user_data)
97+
98+
response = client.post(
99+
'/identity/update',
100+
data=user_data,
101+
)
102+
assert response.status_code == HTTPStatus.FOUND
103+
assert response.get('Location') == '/identity/update'
104+
assert_correct_profile(email, user_data)
105+
106+
46107
@pytest.mark.parametrize('page', [
47108
'/robots.txt',
48109
'/humans.txt',

0 commit comments

Comments
 (0)