"""Validator tests for email_group schemas. These are plain unit tests — no DB, no integration driver, no HTTP. They lock in the exact validation rules that both the frontend form (which mirrors them) and the server-side handler assume. """ from __future__ import annotations import pytest from pydantic import ValidationError from app.domains.email_group.schemas import ( EmailAliasCreateRequest, EmailGroupCreateRequest, PasswordResetAdminRequest, ) # ── Mail group creation ── def test_mail_group_accepts_valid_input() -> None: r = EmailGroupCreateRequest( local_part="dev-team", suffix="pingcap.com", name="Dev Team", reason="Team channel", ) assert r.local_part == "dev-team" assert r.suffix == "pingcap.com" @pytest.mark.parametrize( ("local_part", "expected"), (("Dev-Team", "dev-team"), ("+dev", "+dev"), ("-dev", "-dev"), ("a", "a")), ) def test_mail_group_normalises_valid_local_parts(local_part: str, expected: str) -> None: request = EmailGroupCreateRequest( local_part=local_part, suffix="pingcap.com", name="x", reason="x", ) assert request.local_part == expected @pytest.mark.parametrize( "overrides", ( {"local_part": "dev team"}, {"local_part": ".dev"}, {"local_part": "dev."}, {"local_part": "a..b"}, {"local_part": "a" * 65}, {"suffix": "@pingcap.com"}, {"reason": ""}, ), ) def test_mail_group_rejects_invalid_fields(overrides: dict[str, str]) -> None: payload = { "local_part": "dev", "suffix": "pingcap.com", "name": "x", "reason": "x", **overrides, } with pytest.raises(ValidationError): EmailGroupCreateRequest(**payload) # ── Alias ── def test_alias_accepts_valid_input() -> None: r = EmailAliasCreateRequest( target_kind="user", target_email="ada@pingcap.com", alias_local_part="dev", alias_suffix="pingcap.com", reason="shorthand", ) assert r.target_email == "ada@pingcap.com" def test_alias_rejects_invalid_target_kind() -> None: with pytest.raises(ValidationError): EmailAliasCreateRequest( target_kind="other", target_email="a@b.com", alias_local_part="x", alias_suffix="pingcap.com", reason="r", ) def test_alias_requires_at_in_target_email() -> None: with pytest.raises(ValidationError): EmailAliasCreateRequest( target_kind="user", target_email="invalid-email", alias_local_part="x", alias_suffix="pingcap.com", reason="r", ) def test_alias_lowercases_target_email() -> None: r = EmailAliasCreateRequest( target_kind="user", target_email="ADA@pingcap.com", alias_local_part="dev", alias_suffix="pingcap.com", reason="r", ) assert r.target_email == "ada@pingcap.com" # ── Admin password reset ── def test_admin_password_reset_requires_reason() -> None: with pytest.raises(ValidationError): PasswordResetAdminRequest( target_person_id="00000000-0000-0000-0000-000000000001", reason="x", # too short (<3) ) def test_admin_password_reset_accepts_valid() -> None: r = PasswordResetAdminRequest( target_person_id="00000000-0000-0000-0000-000000000001", reason="User lost 2FA device", ) assert "lost" in r.reason