schon/engine/vibes_auth/tests/test_drf.py
Egor fureunoir Gorbunov 5f5274f9cd Features: 1) Add detailed type annotations across middleware, tests, and utility code; 2) Integrate stricter type-checking configurations in pyproject.toml; 3) Enhance middleware functionality with additional type-safe logic.
Fixes: 1) Correct default values and type handling in util constructors; 2) Resolve missing or ambiguous `cast` operations for dynamic typing in tests and views; 3) Address potential issues with fallback/default handling in middleware.

Extra: 1) Refactor test cases to ensure stricter adherence to typing hints and valid contracts; 2) Update docstrings to align with new type annotations; 3) Cleanup unused imports and add comments for improved maintainability.
2025-12-18 16:44:13 +03:00

224 lines
8.5 KiB
Python

from base64 import urlsafe_b64encode
from io import BytesIO
from typing import Any, cast
from unittest.mock import patch
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken
from engine.vibes_auth.models import User
class DRFAuthViewsTests(TestCase):
def setUp(self):
super().setUp()
self.client = APIClient()
def test_token_obtain_pair_success(self):
user: User = cast(
User,
cast(Any, User.objects).create_user(
email="user@example.com", password="Str0ngPass!word", is_active=True
),
)
url = reverse("vibes_auth:token_create")
resp = self.client.post(
url,
{"email": cast(Any, user).email, "password": "Str0ngPass!word"},
format="json",
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
data = resp.json()
self.assertIn("access", data, data)
self.assertTrue(data["access"], data)
self.assertIn("refresh", data, data)
self.assertTrue(data["refresh"], data)
self.assertEqual(data["user"]["email"], cast(Any, user).email, data)
def test_token_obtain_pair_invalid_credentials(self):
cast(Any, User.objects).create_user(
email="user@example.com", password="Str0ngPass!word", is_active=True
)
url = reverse("vibes_auth:token_create")
resp = self.client.post(
url, {"email": "user@example.com", "password": "wrong"}, format="json"
)
self.assertEqual(resp.status_code, status.HTTP_401_UNAUTHORIZED)
def test_token_obtain_ratelimited(self):
url = reverse("vibes_auth:token_create")
for _ in range(0, 10):
self.client.post(
url, {"email": "user@example.com", "password": "wrong"}, format="json"
)
resp = self.client.post(
url, {"email": "user@example.com", "password": "wrong"}, format="json"
)
self.assertEqual(resp.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
def test_token_refresh_and_verify_flow(self):
user: User = cast(
User,
cast(Any, User.objects).create_user(
email="user@example.com", password="Str0ngPass!word", is_active=True
),
)
tokens = RefreshToken.for_user(user)
refresh_url = reverse("vibes_auth:token_refresh")
resp_refresh = self.client.post(
refresh_url, {"refresh": str(tokens)}, format="json"
)
self.assertEqual(resp_refresh.status_code, status.HTTP_200_OK)
access = resp_refresh.json()["access"]
verify_url = reverse("vibes_auth:token_verify")
resp_verify = self.client.post(verify_url, {"token": access}, format="json")
self.assertEqual(resp_verify.status_code, status.HTTP_200_OK)
self.assertTrue(resp_verify.json()["token"])
self.assertEqual(resp_verify.json()["user"]["email"], cast(Any, user).email)
def test_token_verify_invalid_token(self):
verify_url = reverse("vibes_auth:token_verify")
resp = self.client.post(verify_url, {"token": "malformed"}, format="json")
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("detail", resp.json(), resp.json())
def test_user_create_and_activate_flow(self):
url_create = reverse("vibes_auth:users-list")
payload = {
"email": "new@example.com",
"password": "Str0ngPass!word",
"confirm_password": "Str0ngPass!word",
"first_name": "New",
"last_name": "User",
}
resp = self.client.post(url_create, payload, format="json")
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
user_uuid = resp.json()["uuid"]
user = User.objects.get(uuid=user_uuid)
self.assertFalse(user.is_active)
activate_url = reverse("vibes_auth:users-activate")
uidb64 = urlsafe_b64encode(str(cast(Any, user).uuid).encode()).decode()
token_b64 = urlsafe_b64encode(
str(cast(Any, user).activation_token).encode()
).decode()
resp_act = self.client.post(
activate_url, {"uidb_64": uidb64, "token": token_b64}, format="json"
)
self.assertEqual(resp_act.status_code, status.HTTP_200_OK)
user.refresh_from_db()
self.assertTrue(cast(Any, user).is_active and cast(Any, user).is_verified)
def test_reset_password_triggers_task(self):
user: User = cast(
User,
cast(Any, User.objects).create_user(
email="user@example.com", password="Str0ngPass!word", is_active=True
),
)
with patch(
"engine.vibes_auth.viewsets.send_reset_password_email_task.delay"
) as mocked_delay:
url = reverse("vibes_auth:users-reset-password")
resp = self.client.post(
url, {"email": cast(Any, user).email}, format="json"
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
mocked_delay.assert_called_once()
def test_confirm_password_reset_success(self):
user: User = cast(
User,
cast(Any, User.objects).create_user(
email="user@example.com", password="OldPass!123", is_active=True
),
)
gen = PasswordResetTokenGenerator()
token = gen.make_token(user)
uidb64 = urlsafe_b64encode(str(cast(Any, user).uuid).encode()).decode()
url = reverse("vibes_auth:users-confirm-password-reset")
new_pass = "NewPass!12345"
resp = self.client.post(
url,
{
"uidb_64": uidb64,
"token": token,
"password": new_pass,
"confirm_password": new_pass,
},
format="json",
)
self.assertEqual(resp.status_code, status.HTTP_200_OK, resp.json())
obtain_url = reverse("vibes_auth:token_create")
r2 = self.client.post(
obtain_url,
{"email": cast(Any, user).email, "password": new_pass},
format="json",
)
self.assertEqual(r2.status_code, status.HTTP_200_OK, resp.json())
def test_upload_avatar_permission_enforced(self):
owner: User = cast(
User,
cast(Any, User.objects).create_user(
email="owner@example.com", password="Str0ngPass!word", is_active=True
),
)
stranger: User = cast(
User,
cast(Any, User.objects).create_user(
email="stranger@example.com",
password="Str0ngPass!word",
is_active=True,
),
)
access = str(RefreshToken.for_user(stranger).access_token)
# noinspection PyUnresolvedReferences
cast(Any, self.client).credentials(HTTP_X_EVIBES_AUTH=f"Bearer {access}")
url = reverse(
"vibes_auth:users-upload-avatar", kwargs={"pk": cast(Any, owner).pk}
)
file_content = BytesIO(b"fake image content")
file = SimpleUploadedFile(
"avatar.png", file_content.getvalue(), content_type="image/png"
)
resp = self.client.put(url, {"avatar": file})
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
def test_merge_recently_viewed_permission_enforced(self):
owner: User = cast(
User,
cast(Any, User.objects).create_user(
email="owner@example.com", password="Str0ngPass!word", is_active=True
),
)
stranger: User = cast(
User,
cast(Any, User.objects).create_user(
email="stranger@example.com",
password="Str0ngPass!word",
is_active=True,
),
)
access = str(RefreshToken.for_user(stranger).access_token)
# noinspection PyUnresolvedReferences
cast(Any, self.client).credentials(HTTP_X_EVIBES_AUTH=f"Bearer {access}")
url = reverse(
"vibes_auth:users-merge-recently-viewed",
kwargs={"pk": cast(Any, owner).pk},
)
resp = self.client.put(url, {"product_uuids": []}, format="json")
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)