Files
clubber/tests/unit/test_validation.py
T

198 lines
7.4 KiB
Python
Raw Normal View History

2025-11-21 19:56:03 +01:00
"""Unit tests for validation logic."""
import pytest
from src.validation import (
ValidationError,
validate_email,
validate_phone,
validate_member_type_requirements,
)
2025-11-21 19:56:03 +01:00
class TestEmailValidation:
"""Tests for validate_email function."""
def test_valid_email(self):
"""Test that valid email passes validation."""
validate_email("test@example.com")
# No exception = pass
def test_valid_email_with_subdomain(self):
"""Test that email with subdomain passes validation."""
validate_email("user@mail.example.com")
def test_valid_email_with_plus(self):
"""Test that email with plus sign passes validation."""
validate_email("user+tag@example.com")
def test_invalid_email_no_at(self):
"""Test that email without @ raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid email format"):
validate_email("notanemail")
def test_invalid_email_no_domain(self):
"""Test that email without domain raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid email format"):
validate_email("user@")
def test_invalid_email_no_tld(self):
"""Test that email without TLD raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid email format"):
validate_email("user@example")
def test_none_is_allowed(self):
"""Test that None passes validation (optional field)."""
validate_email(None)
# No exception = pass
def test_empty_string_is_allowed(self):
"""Test that empty string passes validation (optional field)."""
validate_email("")
# No exception = pass
class TestPhoneValidation:
"""Tests for validate_phone function."""
def test_valid_phone_with_plus(self):
"""Test that valid E.164 phone with + passes validation."""
validate_phone("+41791234567")
def test_valid_phone_without_plus(self):
"""Test that valid E.164 phone without + passes validation."""
validate_phone("41791234567")
def test_valid_us_phone(self):
"""Test that valid US phone passes validation."""
validate_phone("+14155551234")
def test_invalid_phone_with_invalid_chars(self):
"""Test that phone with invalid characters raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid phone format"):
validate_phone("abc123")
def test_invalid_phone_too_long(self):
"""Test that phone too long raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid phone format"):
validate_phone("+123456789012345678") # More than 15 digits
def test_invalid_phone_starts_with_zero(self):
"""Test that phone starting with 0 raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid phone format"):
validate_phone("+0791234567")
def test_invalid_phone_has_letters(self):
"""Test that phone with letters raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid phone format"):
validate_phone("+41abc1234567")
def test_none_is_allowed(self):
"""Test that None passes validation (optional field)."""
validate_phone(None)
# No exception = pass
def test_empty_string_is_allowed(self):
"""Test that empty string passes validation (optional field)."""
validate_phone("")
# No exception = pass
class TestMemberTypeRequirements:
"""Tests for validate_member_type_requirements function."""
def test_individual_with_first_name_valid(self):
"""Test INDIVIDUAL with firstName passes."""
validate_member_type_requirements(
member_type="INDIVIDUAL",
first_name="John",
company_name=None
)
def test_individual_without_first_name_invalid(self):
"""Test INDIVIDUAL without firstName fails."""
with pytest.raises(ValidationError, match="firstName is required for individual members"):
validate_member_type_requirements(
member_type="INDIVIDUAL",
first_name=None,
company_name=None
)
def test_individual_with_empty_first_name_invalid(self):
"""Test INDIVIDUAL with empty firstName fails."""
with pytest.raises(ValidationError, match="firstName is required for individual members"):
validate_member_type_requirements(
member_type="INDIVIDUAL",
first_name="",
company_name=None
)
def test_individual_with_whitespace_first_name_invalid(self):
"""Test INDIVIDUAL with whitespace-only firstName fails."""
with pytest.raises(ValidationError, match="firstName is required for individual members"):
validate_member_type_requirements(
member_type="INDIVIDUAL",
first_name=" ",
company_name=None
)
def test_organization_with_company_name_valid(self):
"""Test ORGANIZATION with companyName passes."""
validate_member_type_requirements(
member_type="ORGANIZATION",
first_name=None,
company_name="Acme Corp"
)
def test_organization_without_company_name_invalid(self):
"""Test ORGANIZATION without companyName fails."""
with pytest.raises(ValidationError, match="companyName is required for organization members"):
validate_member_type_requirements(
member_type="ORGANIZATION",
first_name=None,
company_name=None
)
def test_organization_with_empty_company_name_invalid(self):
"""Test ORGANIZATION with empty companyName fails."""
with pytest.raises(ValidationError, match="companyName is required for organization members"):
validate_member_type_requirements(
member_type="ORGANIZATION",
first_name=None,
company_name=""
)
def test_organization_with_whitespace_company_name_invalid(self):
"""Test ORGANIZATION with whitespace-only companyName fails."""
with pytest.raises(ValidationError, match="companyName is required for organization members"):
validate_member_type_requirements(
member_type="ORGANIZATION",
first_name=None,
company_name=" "
)
def test_invalid_member_type_raises_error(self):
"""Test invalid member type raises ValidationError."""
with pytest.raises(ValidationError, match="Invalid member type"):
validate_member_type_requirements(
member_type="INVALID",
first_name="John",
company_name=None
)
def test_organization_can_have_first_name_optional(self):
"""Test ORGANIZATION can optionally have firstName."""
validate_member_type_requirements(
member_type="ORGANIZATION",
first_name="John Doe",
company_name="Acme Corp"
)
def test_individual_can_have_company_name_optional(self):
"""Test INDIVIDUAL can optionally have companyName (edge case)."""
validate_member_type_requirements(
member_type="INDIVIDUAL",
first_name="John",
company_name="Acme Corp"
)