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.
224 lines
8.5 KiB
Python
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)
|