DealDocumentScreening/tests/unit/test_profile_schemas.py

84 lines
2.7 KiB
Python

"""Unit tests for profile pydantic schemas (ticket 002).
Validation is the api boundary for the JSONB columns: language enum, IANA
timezone, notif/dashboard pref shapes, PATCH all-optional semantics.
"""
from __future__ import annotations
import pytest
from pydantic import ValidationError
from contract_check.api.schemas.profile import (
DashboardPrefs,
NotificationPrefs,
UserProfile,
UserProfilePatch,
)
class TestUserProfile:
def test_defaults(self) -> None:
profile = UserProfile()
assert profile.language is None
assert profile.timezone is None
assert profile.notif_prefs == NotificationPrefs(
report_ready=True, security=True, marketing=False
)
assert profile.dashboard_prefs == DashboardPrefs(
severity_filter="all", per_page=10, density="comfortable"
)
def test_valid_language_and_timezone(self) -> None:
profile = UserProfile(language="ru", timezone="Europe/Minsk")
assert profile.language == "ru"
assert profile.timezone == "Europe/Minsk"
def test_invalid_language_rejected(self) -> None:
with pytest.raises(ValidationError):
UserProfile(language="de")
def test_invalid_timezone_rejected(self) -> None:
with pytest.raises(ValidationError):
UserProfile(timezone="Mars/Olympus_Mons")
def test_empty_timezone_normalized_to_none(self) -> None:
assert UserProfile(timezone="").timezone is None
@pytest.mark.parametrize(
("field", "value"),
[
("severity_filter", "critical"),
("per_page", 0),
("per_page", 101),
("density", "spacious"),
],
)
def test_dashboard_pref_validation(self, field: str, value: object) -> None:
with pytest.raises(ValidationError):
DashboardPrefs.model_validate({field: value})
def test_unknown_fields_ignored(self) -> None:
profile = UserProfile.model_validate({"language": "en", "hobby": "chess"})
assert profile.language == "en"
assert not hasattr(profile, "hobby")
class TestUserProfilePatch:
def test_empty_patch(self) -> None:
patch = UserProfilePatch()
assert patch.is_empty()
def test_partial_patch(self) -> None:
patch = UserProfilePatch(language="be")
assert patch.language == "be"
assert patch.notif_prefs is None
assert not patch.is_empty()
def test_invalid_timezone_rejected(self) -> None:
with pytest.raises(ValidationError):
UserProfilePatch(timezone="not/a-zone")
def test_nested_prefs_validated(self) -> None:
with pytest.raises(ValidationError):
UserProfilePatch(notif_prefs={"marketing": "maybe"})