forked from cardosofelipe/fast-next-template
- Introduced `pyproject.toml` to centralize backend tool configurations (e.g., Ruff, mypy, coverage, pytest). - Replaced Black, isort, and Flake8 with Ruff for linting, formatting, and import sorting. - Updated `requirements.txt` to include Ruff and remove replaced tools. - Added `Makefile` to streamline development workflows with commands for linting, formatting, type-checking, testing, and cleanup.
169 lines
6.9 KiB
Python
169 lines
6.9 KiB
Python
"""
|
|
Tests for organization schemas (app/schemas/organizations.py).
|
|
|
|
Covers Pydantic validators for:
|
|
- Slug validation (lines 26, 28, 30, 32, 62-70)
|
|
- Name validation (lines 40, 77)
|
|
"""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from app.schemas.organizations import (
|
|
OrganizationBase,
|
|
OrganizationCreate,
|
|
OrganizationUpdate,
|
|
)
|
|
|
|
|
|
class TestOrganizationBaseValidators:
|
|
"""Test validators in OrganizationBase schema."""
|
|
|
|
def test_valid_organization_base(self):
|
|
"""Test that valid data passes validation."""
|
|
org = OrganizationBase(name="Test Organization", slug="test-org")
|
|
assert org.name == "Test Organization"
|
|
assert org.slug == "test-org"
|
|
|
|
def test_slug_none_returns_none(self):
|
|
"""Test that None slug is allowed (covers line 26)."""
|
|
org = OrganizationBase(name="Test Organization", slug=None)
|
|
assert org.slug is None
|
|
|
|
def test_slug_invalid_characters_rejected(self):
|
|
"""Test slug with invalid characters is rejected (covers line 28)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationBase(
|
|
name="Test Organization",
|
|
slug="Test_Org!", # Uppercase and special chars
|
|
)
|
|
errors = exc_info.value.errors()
|
|
assert any(
|
|
"lowercase letters, numbers, and hyphens" in str(e["msg"]) for e in errors
|
|
)
|
|
|
|
def test_slug_starts_with_hyphen_rejected(self):
|
|
"""Test slug starting with hyphen is rejected (covers line 30)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationBase(name="Test Organization", slug="-test-org")
|
|
errors = exc_info.value.errors()
|
|
assert any("cannot start or end with a hyphen" in str(e["msg"]) for e in errors)
|
|
|
|
def test_slug_ends_with_hyphen_rejected(self):
|
|
"""Test slug ending with hyphen is rejected (covers line 30)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationBase(name="Test Organization", slug="test-org-")
|
|
errors = exc_info.value.errors()
|
|
assert any("cannot start or end with a hyphen" in str(e["msg"]) for e in errors)
|
|
|
|
def test_slug_consecutive_hyphens_rejected(self):
|
|
"""Test slug with consecutive hyphens is rejected (covers line 32)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationBase(name="Test Organization", slug="test--org")
|
|
errors = exc_info.value.errors()
|
|
assert any(
|
|
"cannot contain consecutive hyphens" in str(e["msg"]) for e in errors
|
|
)
|
|
|
|
def test_name_whitespace_only_rejected(self):
|
|
"""Test whitespace-only name is rejected (covers line 40)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationBase(name=" ", slug="test-org")
|
|
errors = exc_info.value.errors()
|
|
assert any("name cannot be empty" in str(e["msg"]) for e in errors)
|
|
|
|
def test_name_trimmed(self):
|
|
"""Test that name is trimmed."""
|
|
org = OrganizationBase(name=" Test Organization ", slug="test-org")
|
|
assert org.name == "Test Organization"
|
|
|
|
|
|
class TestOrganizationCreateValidators:
|
|
"""Test OrganizationCreate schema inherits validators."""
|
|
|
|
def test_valid_organization_create(self):
|
|
"""Test that valid data passes validation."""
|
|
org = OrganizationCreate(name="Test Organization", slug="test-org")
|
|
assert org.name == "Test Organization"
|
|
assert org.slug == "test-org"
|
|
|
|
def test_slug_validation_inherited(self):
|
|
"""Test that slug validation is inherited from base."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationCreate(name="Test", slug="Invalid_Slug!")
|
|
errors = exc_info.value.errors()
|
|
assert any(
|
|
"lowercase letters, numbers, and hyphens" in str(e["msg"]) for e in errors
|
|
)
|
|
|
|
|
|
class TestOrganizationUpdateValidators:
|
|
"""Test validators in OrganizationUpdate schema."""
|
|
|
|
def test_valid_organization_update(self):
|
|
"""Test that valid update data passes validation."""
|
|
org = OrganizationUpdate(name="Updated Name", slug="updated-slug")
|
|
assert org.name == "Updated Name"
|
|
assert org.slug == "updated-slug"
|
|
|
|
def test_slug_none_returns_none(self):
|
|
"""Test that None slug is allowed in update (covers line 62)."""
|
|
org = OrganizationUpdate(slug=None)
|
|
assert org.slug is None
|
|
|
|
def test_update_slug_invalid_characters_rejected(self):
|
|
"""Test update slug with invalid characters is rejected (covers line 64)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationUpdate(slug="Test_Org!")
|
|
errors = exc_info.value.errors()
|
|
assert any(
|
|
"lowercase letters, numbers, and hyphens" in str(e["msg"]) for e in errors
|
|
)
|
|
|
|
def test_update_slug_starts_with_hyphen_rejected(self):
|
|
"""Test update slug starting with hyphen is rejected (covers line 66)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationUpdate(slug="-test-org")
|
|
errors = exc_info.value.errors()
|
|
assert any("cannot start or end with a hyphen" in str(e["msg"]) for e in errors)
|
|
|
|
def test_update_slug_ends_with_hyphen_rejected(self):
|
|
"""Test update slug ending with hyphen is rejected (covers line 66)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationUpdate(slug="test-org-")
|
|
errors = exc_info.value.errors()
|
|
assert any("cannot start or end with a hyphen" in str(e["msg"]) for e in errors)
|
|
|
|
def test_update_slug_consecutive_hyphens_rejected(self):
|
|
"""Test update slug with consecutive hyphens is rejected (covers line 68)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationUpdate(slug="test--org")
|
|
errors = exc_info.value.errors()
|
|
assert any(
|
|
"cannot contain consecutive hyphens" in str(e["msg"]) for e in errors
|
|
)
|
|
|
|
def test_update_name_whitespace_only_rejected(self):
|
|
"""Test whitespace-only name in update is rejected (covers line 77)."""
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OrganizationUpdate(name=" ")
|
|
errors = exc_info.value.errors()
|
|
assert any("name cannot be empty" in str(e["msg"]) for e in errors)
|
|
|
|
def test_update_name_none_allowed(self):
|
|
"""Test that None name is allowed in update."""
|
|
org = OrganizationUpdate(name=None)
|
|
assert org.name is None
|
|
|
|
def test_update_name_trimmed(self):
|
|
"""Test that update name is trimmed."""
|
|
org = OrganizationUpdate(name=" Updated Name ")
|
|
assert org.name == "Updated Name"
|
|
|
|
def test_partial_update(self):
|
|
"""Test that partial updates work (all fields optional)."""
|
|
org = OrganizationUpdate(name="New Name")
|
|
assert org.name == "New Name"
|
|
assert org.slug is None
|
|
assert org.description is None
|