refactor: complete bootstrap of ARNES agent harness framework
- Add complete agent harness structure with 8 roles (leader, triager, architect, implementer, reviewer, security, qa, documenter) - Implement strict workflow with 9 stages and mandatory gates - Add comprehensive verification script and runtime status tracking - Create artifact-based evidence system with contracts and schemas - Add agent policy matrix with permissions and anti-cheat rules - Include test suite (44 tests passing) and CI-ready structure - Add documentation: README, HOWTO, CHECKPOINTS, templates - Configure model routing policies and token-aware task assignment - Add BDD/SDD specification guides and feature templates - Include starter pack for quick project onboarding All verification checks pass. Framework ready for production use.
This commit is contained in:
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Unit tests init."""
|
||||
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
1
tests/unit/__init__.py
Normal file
1
tests/unit/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Unit tests init."""
|
||||
BIN
tests/unit/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
tests/unit/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
tests/unit/__pycache__/test_auth.cpython-313.pyc
Normal file
BIN
tests/unit/__pycache__/test_auth.cpython-313.pyc
Normal file
Binary file not shown.
BIN
tests/unit/__pycache__/test_password.cpython-313.pyc
Normal file
BIN
tests/unit/__pycache__/test_password.cpython-313.pyc
Normal file
Binary file not shown.
BIN
tests/unit/__pycache__/test_profile.cpython-313.pyc
Normal file
BIN
tests/unit/__pycache__/test_profile.cpython-313.pyc
Normal file
Binary file not shown.
262
tests/unit/test_auth.py
Normal file
262
tests/unit/test_auth.py
Normal file
@@ -0,0 +1,262 @@
|
||||
"""Unit tests for authentication service."""
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from src.models.auth import LoginRequest, TokenPayload
|
||||
from src.services.auth_service import (
|
||||
AuthService, auth_service,
|
||||
InvalidCredentialsError, AccountLockedError, InvalidTokenError
|
||||
)
|
||||
from src.services.token_service import TokenService
|
||||
from src.services.session_store import SessionStore
|
||||
|
||||
|
||||
class TestAuthService(unittest.TestCase):
|
||||
"""Test cases for AuthService."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.mock_token_service = MagicMock(spec=TokenService)
|
||||
self.mock_session_store = MagicMock(spec=SessionStore)
|
||||
|
||||
self.auth_service = AuthService(
|
||||
token_svc=self.mock_token_service,
|
||||
session_store=self.mock_session_store
|
||||
)
|
||||
|
||||
# Default mock returns
|
||||
self.mock_token_service.create_access_token.return_value = ("access_token_123", "token_id_123")
|
||||
self.mock_token_service.create_refresh_token.return_value = "refresh_token_456"
|
||||
|
||||
def test_login_success(self):
|
||||
"""Test successful login."""
|
||||
request = LoginRequest(email="alice@example.com", password="SecurePass123!")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email') as mock_get_user:
|
||||
mock_get_user.return_value = {
|
||||
"id": "user-001",
|
||||
"email": "alice@example.com",
|
||||
"password_hash": "hashed_password",
|
||||
"role": "user",
|
||||
"active": True
|
||||
}
|
||||
|
||||
with patch.object(self.auth_service, '_verify_password', return_value=True):
|
||||
result = self.auth_service.login(request, "127.0.0.1")
|
||||
|
||||
self.assertTrue(result.success)
|
||||
self.assertEqual(result.access_token, "access_token_123")
|
||||
self.assertEqual(result.refresh_token, "refresh_token_456")
|
||||
self.mock_session_store.create_session.assert_called_once()
|
||||
|
||||
def test_login_invalid_credentials(self):
|
||||
"""Test login with invalid credentials."""
|
||||
request = LoginRequest(email="alice@example.com", password="WrongPassword!")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email') as mock_get_user:
|
||||
mock_get_user.return_value = {
|
||||
"id": "user-001",
|
||||
"email": "alice@example.com",
|
||||
"password_hash": "hashed_password",
|
||||
"role": "user",
|
||||
"active": True
|
||||
}
|
||||
|
||||
with patch.object(self.auth_service, '_verify_password', return_value=False):
|
||||
with self.assertRaises(InvalidCredentialsError):
|
||||
self.auth_service.login(request, "127.0.0.1")
|
||||
|
||||
def test_login_nonexistent_user(self):
|
||||
"""Test login with nonexistent user."""
|
||||
request = LoginRequest(email="nonexistent@test.com", password="AnyPassword123!")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email', return_value=None):
|
||||
with self.assertRaises(InvalidCredentialsError):
|
||||
self.auth_service.login(request, "127.0.0.1")
|
||||
|
||||
def test_login_inactive_account(self):
|
||||
"""Test login with inactive account."""
|
||||
request = LoginRequest(email="alice@example.com", password="SecurePass123!")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email') as mock_get_user:
|
||||
mock_get_user.return_value = {
|
||||
"id": "user-001",
|
||||
"email": "alice@example.com",
|
||||
"password_hash": "hashed_password",
|
||||
"role": "user",
|
||||
"active": False
|
||||
}
|
||||
|
||||
with self.assertRaises(InvalidCredentialsError) as ctx:
|
||||
self.auth_service.login(request, "127.0.0.1")
|
||||
|
||||
self.assertIn("desactivada", str(ctx.exception))
|
||||
|
||||
def test_logout_success(self):
|
||||
"""Test successful logout."""
|
||||
result = self.auth_service.logout("token_id_123", "user-001")
|
||||
|
||||
self.assertTrue(result)
|
||||
self.mock_session_store.revoke_session.assert_called_once_with("token_id_123")
|
||||
|
||||
def test_logout_all_sessions(self):
|
||||
"""Test logout all sessions."""
|
||||
self.mock_session_store.revoke_all_user_sessions.return_value = 3
|
||||
|
||||
result = self.auth_service.logout_all("user-001")
|
||||
|
||||
self.assertEqual(result, 3)
|
||||
self.mock_session_store.revoke_all_user_sessions.assert_called_once_with("user-001")
|
||||
|
||||
def test_refresh_success(self):
|
||||
"""Test successful token refresh."""
|
||||
mock_payload = TokenPayload(
|
||||
sub="user-001",
|
||||
email="alice@example.com",
|
||||
role="user",
|
||||
iat=1234567890,
|
||||
exp=1234567890 + 900,
|
||||
jti="token_id_123",
|
||||
type="refresh"
|
||||
)
|
||||
|
||||
self.mock_token_service.verify_token.return_value = mock_payload
|
||||
self.mock_session_store.is_session_valid.return_value = True
|
||||
self.mock_token_service.create_access_token.return_value = ("new_access_token", "new_token_id")
|
||||
|
||||
result = self.auth_service.refresh("valid_refresh_token")
|
||||
|
||||
self.assertTrue(result.success)
|
||||
self.assertEqual(result.access_token, "new_access_token")
|
||||
|
||||
def test_refresh_invalid_token(self):
|
||||
"""Test refresh with invalid token."""
|
||||
self.mock_token_service.verify_token.side_effect = InvalidTokenError("Invalid token")
|
||||
|
||||
with self.assertRaises(InvalidTokenError):
|
||||
self.auth_service.refresh("invalid_token")
|
||||
|
||||
def test_refresh_revoked_session(self):
|
||||
"""Test refresh with revoked session."""
|
||||
mock_payload = TokenPayload(
|
||||
sub="user-001",
|
||||
email="alice@example.com",
|
||||
role="user",
|
||||
iat=1234567890,
|
||||
exp=1234567890 + 900,
|
||||
jti="token_id_123",
|
||||
type="refresh"
|
||||
)
|
||||
|
||||
self.mock_token_service.verify_token.return_value = mock_payload
|
||||
self.mock_session_store.is_session_valid.return_value = False
|
||||
|
||||
with self.assertRaises(InvalidTokenError):
|
||||
self.auth_service.refresh("valid_refresh_token")
|
||||
|
||||
def test_validate_token_valid(self):
|
||||
"""Test token validation with valid token."""
|
||||
mock_payload = TokenPayload(
|
||||
sub="user-001",
|
||||
email="alice@example.com",
|
||||
role="user",
|
||||
iat=1234567890,
|
||||
exp=1234567890 + 900,
|
||||
jti="token_id_123"
|
||||
)
|
||||
|
||||
self.mock_token_service.verify_token.return_value = mock_payload
|
||||
self.mock_session_store.is_session_valid.return_value = True
|
||||
|
||||
is_valid, payload, error = self.auth_service.validate_token("valid_token")
|
||||
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(payload.sub, "user-001")
|
||||
self.assertIsNone(error)
|
||||
|
||||
def test_validate_token_revoked(self):
|
||||
"""Test token validation with revoked session."""
|
||||
mock_payload = TokenPayload(
|
||||
sub="user-001",
|
||||
email="alice@example.com",
|
||||
role="user",
|
||||
iat=1234567890,
|
||||
exp=1234567890 + 900,
|
||||
jti="token_id_123"
|
||||
)
|
||||
|
||||
self.mock_token_service.verify_token.return_value = mock_payload
|
||||
self.mock_session_store.is_session_valid.return_value = False
|
||||
|
||||
is_valid, payload, error = self.auth_service.validate_token("valid_token")
|
||||
|
||||
self.assertFalse(is_valid)
|
||||
self.assertEqual(error, "Session revoked")
|
||||
|
||||
def test_validate_token_expired(self):
|
||||
"""Test token validation with expired token."""
|
||||
from jwt.exceptions import ExpiredSignatureError
|
||||
|
||||
self.mock_token_service.verify_token.side_effect = ExpiredSignatureError("Token expired")
|
||||
|
||||
is_valid, payload, error = self.auth_service.validate_token("expired_token")
|
||||
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("expired", error.lower())
|
||||
|
||||
|
||||
class TestRateLimiting(unittest.TestCase):
|
||||
"""Test rate limiting functionality."""
|
||||
|
||||
def setUp(self):
|
||||
self.auth_service = AuthService()
|
||||
|
||||
def test_rate_limit_allows_first_attempts(self):
|
||||
"""Test rate limit allows initial attempts."""
|
||||
import time
|
||||
ip = "10.0.0.1"
|
||||
|
||||
# Clear any existing rate limit data
|
||||
self.auth_service._rate_limit_store = {ip: []}
|
||||
|
||||
request = LoginRequest(email="test@test.com", password="wrong")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email', return_value=None):
|
||||
# First 9 attempts should raise InvalidCredentialsError (not locked)
|
||||
for i in range(9):
|
||||
try:
|
||||
self.auth_service.login(request, ip)
|
||||
except InvalidCredentialsError:
|
||||
pass # Expected - user doesn't exist
|
||||
except AccountLockedError:
|
||||
self.fail("Should not be locked after 9 attempts")
|
||||
|
||||
# 10th attempt should still raise InvalidCredentialsError (not locked yet)
|
||||
try:
|
||||
self.auth_service.login(request, ip)
|
||||
except InvalidCredentialsError:
|
||||
pass # Still not locked
|
||||
except AccountLockedError:
|
||||
self.fail("Should not be locked after 10 attempts")
|
||||
|
||||
# Verify rate limit counter is at 10
|
||||
self.assertEqual(len(self.auth_service._rate_limit_store[ip]), 10)
|
||||
|
||||
def test_rate_limit_blocks_after_threshold(self):
|
||||
"""Test rate limit blocks after threshold."""
|
||||
import time
|
||||
ip = "10.0.0.2"
|
||||
|
||||
# Pre-fill rate limit
|
||||
now = time.time()
|
||||
self.auth_service._rate_limit_store[ip] = [now - 50] * 10
|
||||
|
||||
request = LoginRequest(email="test@test.com", password="wrong")
|
||||
|
||||
with patch.object(self.auth_service, '_get_user_by_email', return_value=None):
|
||||
with self.assertRaises(AccountLockedError):
|
||||
self.auth_service.login(request, ip)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
185
tests/unit/test_password.py
Normal file
185
tests/unit/test_password.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""Unit tests for password service."""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
import unittest
|
||||
from src.services.password_service import PasswordService
|
||||
|
||||
|
||||
class TestPasswordValidator(unittest.TestCase):
|
||||
"""Tests for password validation."""
|
||||
|
||||
def setUp(self):
|
||||
self.service = PasswordService()
|
||||
|
||||
def test_valid_password_all_requirements(self):
|
||||
"""Test password with all requirements met."""
|
||||
is_valid, error = self.service.validate_password_strength("Password123!")
|
||||
self.assertTrue(is_valid)
|
||||
self.assertEqual(error, "")
|
||||
|
||||
def test_password_too_short(self):
|
||||
"""Test password shorter than 8 characters."""
|
||||
is_valid, error = self.service.validate_password_strength("Pass1!")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("al menos 8 caracteres", error)
|
||||
|
||||
def test_password_too_long(self):
|
||||
"""Test password longer than 128 characters."""
|
||||
long_pass = "A" * 129 + "a1!"
|
||||
is_valid, error = self.service.validate_password_strength(long_pass)
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("máximo 128 caracteres", error)
|
||||
|
||||
def test_password_no_uppercase(self):
|
||||
"""Test password without uppercase letter."""
|
||||
is_valid, error = self.service.validate_password_strength("password123!")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("al menos una mayúscula", error)
|
||||
|
||||
def test_password_no_lowercase(self):
|
||||
"""Test password without lowercase letter."""
|
||||
is_valid, error = self.service.validate_password_strength("PASSWORD123!")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("al menos una minúscula", error)
|
||||
|
||||
def test_password_no_number(self):
|
||||
"""Test password without number."""
|
||||
is_valid, error = self.service.validate_password_strength("PasswordABC!")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("al menos un número", error)
|
||||
|
||||
def test_password_no_special_char(self):
|
||||
"""Test password without special character."""
|
||||
is_valid, error = self.service.validate_password_strength("Password123")
|
||||
self.assertFalse(is_valid)
|
||||
self.assertIn("carácter especial", error)
|
||||
|
||||
|
||||
class TestPasswordService(unittest.TestCase):
|
||||
"""Tests for password service operations."""
|
||||
|
||||
def setUp(self):
|
||||
self.service = PasswordService()
|
||||
|
||||
def test_change_password_success(self):
|
||||
"""Test successful password change."""
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertIsNone(error)
|
||||
|
||||
def test_change_password_wrong_current(self):
|
||||
"""Test password change with wrong current password."""
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"WrongPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 401)
|
||||
self.assertEqual(error, "La contraseña actual es incorrecta")
|
||||
|
||||
def test_change_password_mismatch(self):
|
||||
"""Test password change with mismatching passwords."""
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"NewPass456@",
|
||||
"DifferentPass789!"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 400)
|
||||
self.assertEqual(error, "Las contraseñas no coinciden")
|
||||
|
||||
def test_change_password_weak(self):
|
||||
"""Test password change with weak password."""
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"weak",
|
||||
"weak"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 400)
|
||||
|
||||
def test_change_password_nonexistent_user(self):
|
||||
"""Test password change for nonexistent user."""
|
||||
success, status, error = self.service.change_password(
|
||||
"nonexistent",
|
||||
"AnyPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 404)
|
||||
self.assertEqual(error, "Usuario no encontrado")
|
||||
|
||||
def test_change_password_reuse_history(self):
|
||||
"""Test password change with password from history."""
|
||||
# First change
|
||||
self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
# Try to reuse current (which is now in history)
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"NewPass456@",
|
||||
"OldPass123!",
|
||||
"OldPass123!"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 400)
|
||||
self.assertIn("no puede ser igual a la anterior", error)
|
||||
|
||||
def test_rate_limit_after_5_attempts(self):
|
||||
"""Test rate limiting after 5 failed attempts."""
|
||||
# Make 5 failed attempts
|
||||
for _ in range(5):
|
||||
self.service.change_password(
|
||||
"user-123",
|
||||
"WrongPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
|
||||
# 6th attempt should be rate limited
|
||||
success, status, error = self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
self.assertFalse(success)
|
||||
self.assertEqual(status, 429)
|
||||
self.assertIn("Demasiados intentos", error)
|
||||
|
||||
def test_sessions_invalidated_after_change(self):
|
||||
"""Test that sessions are invalidated after password change."""
|
||||
# Add some sessions
|
||||
self.service._sessions["user-123"] = ["token1", "token2", "token3"]
|
||||
|
||||
# Change password
|
||||
self.service.change_password(
|
||||
"user-123",
|
||||
"OldPass123!",
|
||||
"NewPass456@",
|
||||
"NewPass456@"
|
||||
)
|
||||
|
||||
# Sessions should be cleared
|
||||
self.assertEqual(len(self.service._sessions.get("user-123", [])), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
131
tests/unit/test_profile.py
Normal file
131
tests/unit/test_profile.py
Normal file
@@ -0,0 +1,131 @@
|
||||
"""Unit tests for profile service using unittest."""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
import unittest
|
||||
from src.models.profile import Profile, UpdateProfileRequest
|
||||
from src.services.profile_service import ProfileService
|
||||
|
||||
|
||||
class TestProfileModel(unittest.TestCase):
|
||||
"""Tests for Profile model."""
|
||||
|
||||
def test_profile_creation(self):
|
||||
"""Test creating a profile."""
|
||||
profile = Profile(id="user-1", name="Juan Pérez", language="es")
|
||||
self.assertEqual(profile.id, "user-1")
|
||||
self.assertEqual(profile.name, "Juan Pérez")
|
||||
self.assertEqual(profile.language, "es")
|
||||
|
||||
def test_profile_name_validation_letters(self):
|
||||
"""Test valid name with letters only."""
|
||||
profile = Profile(id="user-1", name="José García")
|
||||
self.assertEqual(profile.name, "José García")
|
||||
|
||||
def test_profile_name_validation_invalid_chars(self):
|
||||
"""Test invalid name with special characters."""
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
Profile(id="user-1", name="Juan@123!")
|
||||
self.assertIn("Nombre inválido", str(ctx.exception))
|
||||
|
||||
def test_profile_avatar_url_valid(self):
|
||||
"""Test valid avatar URL."""
|
||||
profile = Profile(id="user-1", name="Test", avatar_url="https://cdn.example.com/img.jpg")
|
||||
self.assertEqual(profile.avatar_url, "https://cdn.example.com/img.jpg")
|
||||
|
||||
def test_profile_avatar_url_invalid_protocol(self):
|
||||
"""Test invalid avatar URL protocol."""
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
Profile(id="user-1", name="Test", avatar_url="ftp://malicious.com/file.jpg")
|
||||
self.assertIn("Solo se permiten", str(ctx.exception))
|
||||
|
||||
def test_profile_language_values(self):
|
||||
"""Test valid language values."""
|
||||
for lang in ["en", "es", "fr", "de"]:
|
||||
profile = Profile(id="user-1", name="Test", language=lang)
|
||||
self.assertEqual(profile.language, lang)
|
||||
|
||||
|
||||
class TestUpdateProfileRequest(unittest.TestCase):
|
||||
"""Tests for UpdateProfileRequest model."""
|
||||
|
||||
def test_partial_update_name_only(self):
|
||||
"""Test updating only name."""
|
||||
request = UpdateProfileRequest(name="Pedro")
|
||||
self.assertEqual(request.name, "Pedro")
|
||||
self.assertIsNone(request.avatar_url)
|
||||
self.assertIsNone(request.language)
|
||||
|
||||
def test_partial_update_all_fields(self):
|
||||
"""Test updating all fields."""
|
||||
request = UpdateProfileRequest(
|
||||
name="Pedro",
|
||||
avatar_url="https://cdn.example.com/new.jpg",
|
||||
language="en"
|
||||
)
|
||||
self.assertEqual(request.name, "Pedro")
|
||||
self.assertEqual(request.avatar_url, "https://cdn.example.com/new.jpg")
|
||||
self.assertEqual(request.language, "en")
|
||||
|
||||
|
||||
class TestProfileService(unittest.TestCase):
|
||||
"""Tests for ProfileService."""
|
||||
|
||||
def setUp(self):
|
||||
"""Create a fresh service instance."""
|
||||
self.service = ProfileService()
|
||||
|
||||
def test_get_existing_profile(self):
|
||||
"""Test getting an existing profile."""
|
||||
profile, status, error = self.service.get_profile("user-123")
|
||||
self.assertEqual(status, 200)
|
||||
self.assertIsNotNone(profile)
|
||||
self.assertEqual(profile.id, "user-123")
|
||||
self.assertIsNone(error)
|
||||
|
||||
def test_get_nonexistent_profile(self):
|
||||
"""Test getting a nonexistent profile."""
|
||||
profile, status, error = self.service.get_profile("nonexistent")
|
||||
self.assertEqual(status, 404)
|
||||
self.assertIsNone(profile)
|
||||
self.assertEqual(error, "Usuario no encontrado")
|
||||
|
||||
def test_update_profile_name(self):
|
||||
"""Test updating profile name."""
|
||||
request = UpdateProfileRequest(name="Pedro")
|
||||
profile, status, error = self.service.update_profile("user-123", request)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(profile.name, "Pedro")
|
||||
|
||||
def test_update_profile_avatar(self):
|
||||
"""Test updating profile avatar."""
|
||||
request = UpdateProfileRequest(avatar_url="https://cdn.example.com/new.jpg")
|
||||
profile, status, error = self.service.update_profile("user-123", request)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(profile.avatar_url, "https://cdn.example.com/new.jpg")
|
||||
|
||||
def test_update_profile_language(self):
|
||||
"""Test updating profile language."""
|
||||
request = UpdateProfileRequest(language="fr")
|
||||
profile, status, error = self.service.update_profile("user-123", request)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(profile.language, "fr")
|
||||
|
||||
def test_update_nonexistent_profile(self):
|
||||
"""Test updating a nonexistent profile."""
|
||||
request = UpdateProfileRequest(name="Test")
|
||||
profile, status, error = self.service.update_profile("nonexistent", request)
|
||||
self.assertEqual(status, 404)
|
||||
self.assertEqual(error, "Usuario no encontrado")
|
||||
|
||||
def test_create_profile(self):
|
||||
"""Test creating a new profile."""
|
||||
profile = self.service.create_profile("user-new", "New User", "https://cdn.com/img.jpg", "es")
|
||||
self.assertEqual(profile.id, "user-new")
|
||||
self.assertEqual(profile.name, "New User")
|
||||
self.assertEqual(profile.language, "es")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user