84 lines
2.7 KiB
Python
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"})
|