Tests include: - User model validation (22 tests) - UserGroup, User, UserCreate, UserUpdate models - Field validation, email validation, password strength - CamelCase alias support - Sync UsersEndpoint (24 tests) - List, get, create, update, delete operations - Search functionality - Pagination and filtering - Error handling and validation - Data normalization - Async AsyncUsersEndpoint (24 tests) - Complete async coverage matching sync API - All CRUD operations tested - Validation and error handling All 70 tests passing. Achieves >95% coverage for Users API code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
404 lines
14 KiB
Python
404 lines
14 KiB
Python
"""Tests for User data models."""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from wikijs.models import User, UserCreate, UserGroup, UserUpdate
|
|
|
|
|
|
class TestUserGroup:
|
|
"""Test UserGroup model."""
|
|
|
|
def test_user_group_creation(self):
|
|
"""Test creating a valid user group."""
|
|
group = UserGroup(id=1, name="Administrators")
|
|
assert group.id == 1
|
|
assert group.name == "Administrators"
|
|
|
|
def test_user_group_required_fields(self):
|
|
"""Test that required fields are enforced."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserGroup(id=1)
|
|
assert "name" in str(exc_info.value)
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserGroup(name="Administrators")
|
|
assert "id" in str(exc_info.value)
|
|
|
|
|
|
class TestUser:
|
|
"""Test User model."""
|
|
|
|
def test_user_creation_minimal(self):
|
|
"""Test creating a user with minimal required fields."""
|
|
user = User(
|
|
id=1,
|
|
name="John Doe",
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert user.id == 1
|
|
assert user.name == "John Doe"
|
|
assert user.email == "john@example.com"
|
|
assert user.is_active is True
|
|
assert user.is_system is False
|
|
assert user.is_verified is False
|
|
assert user.groups == []
|
|
|
|
def test_user_creation_full(self):
|
|
"""Test creating a user with all fields."""
|
|
groups = [
|
|
UserGroup(id=1, name="Administrators"),
|
|
UserGroup(id=2, name="Editors"),
|
|
]
|
|
user = User(
|
|
id=1,
|
|
name="John Doe",
|
|
email="john@example.com",
|
|
provider_key="local",
|
|
is_system=False,
|
|
is_active=True,
|
|
is_verified=True,
|
|
location="New York",
|
|
job_title="Senior Developer",
|
|
timezone="America/New_York",
|
|
groups=groups,
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
last_login_at="2024-01-15T12:00:00Z",
|
|
)
|
|
assert user.id == 1
|
|
assert user.name == "John Doe"
|
|
assert user.email == "john@example.com"
|
|
assert user.provider_key == "local"
|
|
assert user.is_system is False
|
|
assert user.is_active is True
|
|
assert user.is_verified is True
|
|
assert user.location == "New York"
|
|
assert user.job_title == "Senior Developer"
|
|
assert user.timezone == "America/New_York"
|
|
assert len(user.groups) == 2
|
|
assert user.groups[0].name == "Administrators"
|
|
assert user.last_login_at == "2024-01-15T12:00:00Z"
|
|
|
|
def test_user_camel_case_alias(self):
|
|
"""Test that camelCase aliases work."""
|
|
user = User(
|
|
id=1,
|
|
name="John Doe",
|
|
email="john@example.com",
|
|
providerKey="local",
|
|
isSystem=False,
|
|
isActive=True,
|
|
isVerified=True,
|
|
jobTitle="Developer",
|
|
createdAt="2024-01-01T00:00:00Z",
|
|
updatedAt="2024-01-01T00:00:00Z",
|
|
lastLoginAt="2024-01-15T12:00:00Z",
|
|
)
|
|
assert user.provider_key == "local"
|
|
assert user.is_system is False
|
|
assert user.is_active is True
|
|
assert user.is_verified is True
|
|
assert user.job_title == "Developer"
|
|
assert user.last_login_at == "2024-01-15T12:00:00Z"
|
|
|
|
def test_user_required_fields(self):
|
|
"""Test that required fields are enforced."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(name="John Doe", email="john@example.com")
|
|
assert "id" in str(exc_info.value)
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(id=1, email="john@example.com")
|
|
assert "name" in str(exc_info.value)
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(id=1, name="John Doe")
|
|
assert "email" in str(exc_info.value)
|
|
|
|
def test_user_email_validation(self):
|
|
"""Test email validation."""
|
|
# Valid email
|
|
user = User(
|
|
id=1,
|
|
name="John Doe",
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert user.email == "john@example.com"
|
|
|
|
# Invalid email
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(
|
|
id=1,
|
|
name="John Doe",
|
|
email="not-an-email",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert "email" in str(exc_info.value).lower()
|
|
|
|
def test_user_name_validation(self):
|
|
"""Test name validation."""
|
|
# Too short
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(
|
|
id=1,
|
|
name="J",
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert "at least 2 characters" in str(exc_info.value)
|
|
|
|
# Too long
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(
|
|
id=1,
|
|
name="x" * 256,
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert "cannot exceed 255 characters" in str(exc_info.value)
|
|
|
|
# Empty
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
User(
|
|
id=1,
|
|
name="",
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert "cannot be empty" in str(exc_info.value)
|
|
|
|
# Whitespace trimming
|
|
user = User(
|
|
id=1,
|
|
name=" John Doe ",
|
|
email="john@example.com",
|
|
created_at="2024-01-01T00:00:00Z",
|
|
updated_at="2024-01-01T00:00:00Z",
|
|
)
|
|
assert user.name == "John Doe"
|
|
|
|
|
|
class TestUserCreate:
|
|
"""Test UserCreate model."""
|
|
|
|
def test_user_create_minimal(self):
|
|
"""Test creating user with minimal required fields."""
|
|
user_data = UserCreate(
|
|
email="john@example.com", name="John Doe", password_raw="secret123"
|
|
)
|
|
assert user_data.email == "john@example.com"
|
|
assert user_data.name == "John Doe"
|
|
assert user_data.password_raw == "secret123"
|
|
assert user_data.provider_key == "local"
|
|
assert user_data.groups == []
|
|
assert user_data.must_change_password is False
|
|
assert user_data.send_welcome_email is True
|
|
|
|
def test_user_create_full(self):
|
|
"""Test creating user with all fields."""
|
|
user_data = UserCreate(
|
|
email="john@example.com",
|
|
name="John Doe",
|
|
password_raw="secret123",
|
|
provider_key="ldap",
|
|
groups=[1, 2, 3],
|
|
must_change_password=True,
|
|
send_welcome_email=False,
|
|
location="New York",
|
|
job_title="Developer",
|
|
timezone="America/New_York",
|
|
)
|
|
assert user_data.email == "john@example.com"
|
|
assert user_data.name == "John Doe"
|
|
assert user_data.password_raw == "secret123"
|
|
assert user_data.provider_key == "ldap"
|
|
assert user_data.groups == [1, 2, 3]
|
|
assert user_data.must_change_password is True
|
|
assert user_data.send_welcome_email is False
|
|
assert user_data.location == "New York"
|
|
assert user_data.job_title == "Developer"
|
|
assert user_data.timezone == "America/New_York"
|
|
|
|
def test_user_create_camel_case_alias(self):
|
|
"""Test that camelCase aliases work."""
|
|
user_data = UserCreate(
|
|
email="john@example.com",
|
|
name="John Doe",
|
|
passwordRaw="secret123",
|
|
providerKey="ldap",
|
|
mustChangePassword=True,
|
|
sendWelcomeEmail=False,
|
|
jobTitle="Developer",
|
|
)
|
|
assert user_data.password_raw == "secret123"
|
|
assert user_data.provider_key == "ldap"
|
|
assert user_data.must_change_password is True
|
|
assert user_data.send_welcome_email is False
|
|
assert user_data.job_title == "Developer"
|
|
|
|
def test_user_create_required_fields(self):
|
|
"""Test that required fields are enforced."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(name="John Doe", password_raw="secret123")
|
|
assert "email" in str(exc_info.value)
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", password_raw="secret123")
|
|
assert "name" in str(exc_info.value)
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", name="John Doe")
|
|
# Pydantic uses the field alias in error messages
|
|
assert "passwordRaw" in str(exc_info.value) or "password_raw" in str(
|
|
exc_info.value
|
|
)
|
|
|
|
def test_user_create_email_validation(self):
|
|
"""Test email validation."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="not-an-email", name="John Doe", password_raw="secret123")
|
|
assert "email" in str(exc_info.value).lower()
|
|
|
|
def test_user_create_name_validation(self):
|
|
"""Test name validation."""
|
|
# Too short
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", name="J", password_raw="secret123")
|
|
assert "at least 2 characters" in str(exc_info.value)
|
|
|
|
# Too long
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(
|
|
email="john@example.com", name="x" * 256, password_raw="secret123"
|
|
)
|
|
assert "cannot exceed 255 characters" in str(exc_info.value)
|
|
|
|
# Empty
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", name="", password_raw="secret123")
|
|
assert "cannot be empty" in str(exc_info.value)
|
|
|
|
def test_user_create_password_validation(self):
|
|
"""Test password validation."""
|
|
# Too short
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", name="John Doe", password_raw="123")
|
|
assert "at least 6 characters" in str(exc_info.value)
|
|
|
|
# Too long
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(
|
|
email="john@example.com", name="John Doe", password_raw="x" * 256
|
|
)
|
|
assert "cannot exceed 255 characters" in str(exc_info.value)
|
|
|
|
# Empty
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserCreate(email="john@example.com", name="John Doe", password_raw="")
|
|
assert "cannot be empty" in str(exc_info.value)
|
|
|
|
|
|
class TestUserUpdate:
|
|
"""Test UserUpdate model."""
|
|
|
|
def test_user_update_all_none(self):
|
|
"""Test creating empty update."""
|
|
user_data = UserUpdate()
|
|
assert user_data.name is None
|
|
assert user_data.email is None
|
|
assert user_data.password_raw is None
|
|
assert user_data.location is None
|
|
assert user_data.job_title is None
|
|
assert user_data.timezone is None
|
|
assert user_data.groups is None
|
|
assert user_data.is_active is None
|
|
assert user_data.is_verified is None
|
|
|
|
def test_user_update_partial(self):
|
|
"""Test partial updates."""
|
|
user_data = UserUpdate(name="Jane Doe", email="jane@example.com")
|
|
assert user_data.name == "Jane Doe"
|
|
assert user_data.email == "jane@example.com"
|
|
assert user_data.password_raw is None
|
|
assert user_data.location is None
|
|
|
|
def test_user_update_full(self):
|
|
"""Test full update."""
|
|
user_data = UserUpdate(
|
|
name="Jane Doe",
|
|
email="jane@example.com",
|
|
password_raw="newsecret123",
|
|
location="San Francisco",
|
|
job_title="Senior Developer",
|
|
timezone="America/Los_Angeles",
|
|
groups=[1, 2],
|
|
is_active=False,
|
|
is_verified=True,
|
|
)
|
|
assert user_data.name == "Jane Doe"
|
|
assert user_data.email == "jane@example.com"
|
|
assert user_data.password_raw == "newsecret123"
|
|
assert user_data.location == "San Francisco"
|
|
assert user_data.job_title == "Senior Developer"
|
|
assert user_data.timezone == "America/Los_Angeles"
|
|
assert user_data.groups == [1, 2]
|
|
assert user_data.is_active is False
|
|
assert user_data.is_verified is True
|
|
|
|
def test_user_update_camel_case_alias(self):
|
|
"""Test that camelCase aliases work."""
|
|
user_data = UserUpdate(
|
|
passwordRaw="newsecret123",
|
|
jobTitle="Senior Developer",
|
|
isActive=False,
|
|
isVerified=True,
|
|
)
|
|
assert user_data.password_raw == "newsecret123"
|
|
assert user_data.job_title == "Senior Developer"
|
|
assert user_data.is_active is False
|
|
assert user_data.is_verified is True
|
|
|
|
def test_user_update_email_validation(self):
|
|
"""Test email validation."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(email="not-an-email")
|
|
assert "email" in str(exc_info.value).lower()
|
|
|
|
def test_user_update_name_validation(self):
|
|
"""Test name validation."""
|
|
# Too short
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(name="J")
|
|
assert "at least 2 characters" in str(exc_info.value)
|
|
|
|
# Too long
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(name="x" * 256)
|
|
assert "cannot exceed 255 characters" in str(exc_info.value)
|
|
|
|
# Empty
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(name="")
|
|
assert "cannot be empty" in str(exc_info.value)
|
|
|
|
def test_user_update_password_validation(self):
|
|
"""Test password validation."""
|
|
# Too short
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(password_raw="123")
|
|
assert "at least 6 characters" in str(exc_info.value)
|
|
|
|
# Too long
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UserUpdate(password_raw="x" * 256)
|
|
assert "cannot exceed 255 characters" in str(exc_info.value)
|