"""Tests for user serializers""" from datetime import datetime, timedelta from apps.core.models import BackgroundJob, JobStatus from apps.user.serializers import ( CurrentUserProfileSerializer, CurrentUserSerializer, LoginSerializer, PasswordChangeSerializer, ProfileUpdateSerializer, TokenSerializer, UserManagementSerializer, UserRegistrationSerializer, UserSerializer, UserUpdateSerializer, ) from django.contrib.auth import get_user_model from django.test import TestCase from django.utils import timezone from faker import Faker from .factories import ProfileFactory, UserFactory User = get_user_model() fake = Faker("ru_RU") class UserRegistrationSerializerTest(TestCase): """Tests for UserRegistrationSerializer""" def setUp(self): self.password = fake.password(length=12, special_chars=False) self.user_data = { "email": fake.unique.email(), "username": fake.unique.user_name(), "password": self.password, "password_confirm": self.password, "phone": f"+7{fake.numerify('##########')}", } def test_valid_registration_data(self): """Test valid registration data""" serializer = UserRegistrationSerializer(data=self.user_data) self.assertTrue(serializer.is_valid()) def test_passwords_do_not_match(self): """Test validation fails when passwords don't match""" data = self.user_data.copy() data["password_confirm"] = fake.password(length=12, special_chars=False) serializer = UserRegistrationSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("non_field_errors", serializer.errors) def test_short_password(self): """Test validation fails with short password""" short_password = fake.pystr(min_chars=3, max_chars=5) data = self.user_data.copy() data["password"] = short_password data["password_confirm"] = short_password serializer = UserRegistrationSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("password", serializer.errors) def test_duplicate_email(self): """Test validation fails with duplicate email""" existing_user = UserFactory.create_user() data = self.user_data.copy() data["email"] = existing_user.email serializer = UserRegistrationSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("email", serializer.errors) def test_duplicate_username(self): """Test validation fails with duplicate username""" existing_user = UserFactory.create_user() data = self.user_data.copy() data["username"] = existing_user.username serializer = UserRegistrationSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("username", serializer.errors) def test_create_user(self): """Test user creation through serializer""" serializer = UserRegistrationSerializer(data=self.user_data) self.assertTrue(serializer.is_valid()) user = serializer.save() self.assertIsInstance(user, User) self.assertEqual(user.email, self.user_data["email"]) self.assertEqual(user.username, self.user_data["username"]) self.assertTrue(user.check_password(self.user_data["password"])) class UserSerializerTest(TestCase): """Tests for UserSerializer""" def setUp(self): self.user = UserFactory.create_user() ProfileFactory.create_profile(user=self.user) def test_user_serialization(self): """Test user serialization""" serializer = UserSerializer(self.user) data = serializer.data self.assertEqual(data["id"], self.user.id) self.assertEqual(data["email"], self.user.email) self.assertEqual(data["username"], self.user.username) self.assertEqual(data["phone"], self.user.phone) self.assertEqual(data["is_verified"], self.user.is_verified) self.assertIn("profile", data) self.assertIn("created_at", data) self.assertIn("updated_at", data) def test_read_only_fields(self): """Test that read-only fields are not writable""" read_only_fields = ["id", "is_verified", "created_at", "updated_at"] serializer = UserSerializer() for field_name in read_only_fields: self.assertIn(field_name, serializer.Meta.read_only_fields) class CurrentUserProfileSerializerTest(TestCase): """Tests for current user profile serializer""" def setUp(self): self.user = UserFactory.create_user() self.profile = ProfileFactory.create_profile( user=self.user, first_name="Ivan", mid_name=None, last_name="Petrov", ) def test_middle_name_falls_back_to_empty_string(self): serializer = CurrentUserProfileSerializer(self.profile) self.assertEqual(serializer.data["middle_name"], "") class CurrentUserSerializerTest(TestCase): """Tests for current user serializer fields.""" def setUp(self): self.user = UserFactory.create_user() def test_current_user_contains_is_active(self): serializer = CurrentUserSerializer(self.user) self.assertIn("is_active", serializer.data) self.assertEqual(serializer.data["is_active"], self.user.is_active) class UserManagementSerializerTest(TestCase): """Tests for user management serializer.""" def setUp(self): self.user = UserFactory.create_user() self.user.profile.first_name = None self.user.profile.mid_name = None self.user.profile.last_name = None self.user.profile.save() def test_profile_fields_fallback_to_empty_string(self): serializer = UserManagementSerializer(self.user) self.assertEqual(serializer.data["first_name"], "") self.assertEqual(serializer.data["middle_name"], "") self.assertEqual(serializer.data["last_name"], "") def test_metric_fields_are_derived_from_latest_job(self): now = timezone.make_aware(datetime(2026, 4, 14, 10, 0, 0)) latest_job = BackgroundJob.objects.create( task_id="admin-management-latest", task_name="apps.forms.process", user_id=self.user.id, status=JobStatus.SUCCESS, progress=100, progress_message="Готово", result={"processed": 10}, started_at=now, completed_at=now + timedelta(minutes=3), created_at=now, updated_at=now + timedelta(minutes=3), ) BackgroundJob.objects.create( task_id="admin-management-old", task_name="apps.forms.process", user_id=self.user.id, status=JobStatus.FAILURE, progress=100, progress_message="Ошибка", error="old-error", started_at=now - timedelta(minutes=15), completed_at=now - timedelta(minutes=10), created_at=now - timedelta(minutes=15), updated_at=now - timedelta(minutes=10), ) self.user.latest_job = latest_job serializer = UserManagementSerializer(self.user) self.assertEqual(serializer.data["progress_message"], "Готово") self.assertEqual(serializer.data["result"], {"processed": 10}) self.assertIsNone(serializer.data["error"]) self.assertEqual(serializer.data["is_successful"], True) self.assertEqual(serializer.data["duration"], 180.0) class UserUpdateSerializerTest(TestCase): """Tests for UserUpdateSerializer""" def setUp(self): self.user = UserFactory.create_user() def test_valid_update_data(self): """Test valid update data""" update_data = { "username": fake.unique.user_name(), "phone": f"+7{fake.numerify('##########')}", } serializer = UserUpdateSerializer(self.user, data=update_data, partial=True) self.assertTrue(serializer.is_valid()) updated_user = serializer.save() self.assertEqual(updated_user.username, update_data["username"]) self.assertEqual(updated_user.phone, update_data["phone"]) def test_fields_allowed(self): """Test only allowed fields can be updated""" serializer = UserUpdateSerializer() allowed_fields = ["username", "phone"] self.assertEqual(set(serializer.Meta.fields), set(allowed_fields)) class ProfileUpdateSerializerTest(TestCase): """Tests for ProfileUpdateSerializer""" def setUp(self): self.user = UserFactory.create_user() self.profile = ProfileFactory.create_profile(user=self.user) def test_valid_profile_update_data(self): """Test valid profile update data""" update_data = { "first_name": fake.first_name(), "mid_name": fake.first_name(), "last_name": fake.last_name(), "bio": fake.text(max_nb_chars=200), "date_of_birth": str(fake.date_of_birth(minimum_age=18, maximum_age=80)), } serializer = ProfileUpdateSerializer( self.profile, data=update_data, partial=True ) self.assertTrue(serializer.is_valid()) updated_profile = serializer.save() self.assertEqual(updated_profile.first_name, update_data["first_name"]) self.assertEqual(updated_profile.mid_name, update_data["mid_name"]) self.assertEqual(updated_profile.last_name, update_data["last_name"]) self.assertEqual(updated_profile.bio, update_data["bio"]) def test_fields_allowed(self): """Test only allowed fields can be updated""" serializer = ProfileUpdateSerializer() allowed_fields = [ "first_name", "mid_name", "last_name", "bio", "avatar", "date_of_birth", ] self.assertEqual(set(serializer.Meta.fields), set(allowed_fields)) class LoginSerializerTest(TestCase): """Tests for LoginSerializer""" def setUp(self): self.login_data = { "username": fake.user_name(), "password": fake.password(length=12, special_chars=False), } def test_valid_login_data(self): """Test valid login data""" serializer = LoginSerializer(data=self.login_data) self.assertTrue(serializer.is_valid()) def test_missing_username(self): """Test validation fails without username""" data = {"password": fake.password(length=12, special_chars=False)} serializer = LoginSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("username", serializer.errors) def test_missing_password(self): """Test validation fails without password""" data = {"username": fake.user_name()} serializer = LoginSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("password", serializer.errors) class TokenSerializerTest(TestCase): """Tests for TokenSerializer""" def test_valid_token_data(self): """Test valid token data""" token_data = { "access": fake.pystr(min_chars=50, max_chars=100), "refresh": fake.pystr(min_chars=50, max_chars=100), } serializer = TokenSerializer(data=token_data) self.assertTrue(serializer.is_valid()) def test_missing_access_token(self): """Test validation fails without access token""" data = {"refresh": fake.pystr(min_chars=50, max_chars=100)} serializer = TokenSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("access", serializer.errors) def test_missing_refresh_token(self): """Test validation fails without refresh token""" data = {"access": fake.pystr(min_chars=50, max_chars=100)} serializer = TokenSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("refresh", serializer.errors) class PasswordChangeSerializerTest(TestCase): """Tests for PasswordChangeSerializer""" def setUp(self): self.old_password = fake.password(length=12, special_chars=False) self.new_password = fake.password(length=12, special_chars=False) self.password_data = { "old_password": self.old_password, "new_password": self.new_password, "new_password_confirm": self.new_password, } def test_valid_password_change_data(self): """Test valid password change data""" serializer = PasswordChangeSerializer(data=self.password_data) self.assertTrue(serializer.is_valid()) def test_passwords_do_not_match(self): """Test validation fails when new passwords don't match""" data = self.password_data.copy() data["new_password_confirm"] = fake.password(length=12, special_chars=False) serializer = PasswordChangeSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("non_field_errors", serializer.errors) def test_short_new_password(self): """Test validation fails with short new password""" short_password = fake.pystr(min_chars=3, max_chars=5) data = self.password_data.copy() data["new_password"] = short_password data["new_password_confirm"] = short_password serializer = PasswordChangeSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("new_password", serializer.errors) def test_missing_old_password(self): """Test validation fails without old password""" new_password = fake.password(length=12, special_chars=False) data = {"new_password": new_password, "new_password_confirm": new_password} serializer = PasswordChangeSerializer(data=data) self.assertFalse(serializer.is_valid()) self.assertIn("old_password", serializer.errors)