"""Tests for input validation."""
import pytest
from pydantic import ValidationError
from mcp_workboard_crunchtools.errors import InvalidGoalIdError, InvalidUserIdError
from mcp_workboard_crunchtools.models import (
CreateUserInput,
UpdateUserInput,
validate_goal_id,
validate_user_id,
)
class TestUserIdValidation:
"""Tests for user_id validation."""
def test_valid_user_id(self) -> None:
"""Valid positive integer should pass."""
assert validate_user_id(123) == 123
def test_valid_user_id_large(self) -> None:
"""Large positive integer should pass."""
assert validate_user_id(999999) == 999999
def test_invalid_user_id_zero(self) -> None:
"""Zero should fail."""
with pytest.raises(InvalidUserIdError):
validate_user_id(0)
def test_invalid_user_id_negative(self) -> None:
"""Negative integer should fail."""
with pytest.raises(InvalidUserIdError):
validate_user_id(-1)
class TestGoalIdValidation:
"""Tests for goal_id validation."""
def test_valid_goal_id(self) -> None:
"""Valid positive integer should pass."""
assert validate_goal_id(456) == 456
def test_invalid_goal_id_zero(self) -> None:
"""Zero should fail."""
with pytest.raises(InvalidGoalIdError):
validate_goal_id(0)
def test_invalid_goal_id_negative(self) -> None:
"""Negative integer should fail."""
with pytest.raises(InvalidGoalIdError):
validate_goal_id(-1)
class TestCreateUserInput:
"""Tests for CreateUserInput model."""
def test_valid_user(self) -> None:
"""Valid user input should pass."""
user = CreateUserInput(
first_name="John",
last_name="Doe",
email="john.doe@example.com",
)
assert user.first_name == "John"
assert user.last_name == "Doe"
assert user.email == "john.doe@example.com"
assert user.designation is None
def test_valid_user_with_designation(self) -> None:
"""Valid user with designation should pass."""
user = CreateUserInput(
first_name="Jane",
last_name="Smith",
email="jane@example.com",
designation="VP Engineering",
)
assert user.designation == "VP Engineering"
def test_invalid_email(self) -> None:
"""Invalid email should fail."""
with pytest.raises(ValidationError):
CreateUserInput(
first_name="John",
last_name="Doe",
email="not-an-email",
)
def test_empty_first_name(self) -> None:
"""Empty first name should fail."""
with pytest.raises(ValidationError):
CreateUserInput(
first_name="",
last_name="Doe",
email="john@example.com",
)
def test_name_too_long(self) -> None:
"""Name exceeding max length should fail."""
with pytest.raises(ValidationError):
CreateUserInput(
first_name="a" * 256,
last_name="Doe",
email="john@example.com",
)
def test_extra_fields_rejected(self) -> None:
"""Extra fields should be rejected."""
with pytest.raises(ValidationError):
CreateUserInput(
first_name="John",
last_name="Doe",
email="john@example.com",
admin=True, # type: ignore[call-arg]
)
class TestUpdateUserInput:
"""Tests for UpdateUserInput model."""
def test_partial_update(self) -> None:
"""Partial update with only some fields should pass."""
update = UpdateUserInput(first_name="Jane")
assert update.first_name == "Jane"
assert update.last_name is None
assert update.email is None
assert update.designation is None
def test_all_fields_none(self) -> None:
"""All fields None should be valid (checked at tool level)."""
update = UpdateUserInput()
assert update.first_name is None
assert update.email is None
def test_extra_fields_rejected(self) -> None:
"""Extra fields should be rejected."""
with pytest.raises(ValidationError):
UpdateUserInput(
first_name="Jane",
role="admin", # type: ignore[call-arg]
)