"""Auth DTOs + current-user serializer (no DB / no Flask context).""" from __future__ import annotations from uuid import uuid4 import pytest from pydantic import ValidationError from mimic.api._helpers import serialize_current_user from mimic.auth.identity import AuthUser from mimic.db.types import UserType from mimic.rbac.matrix import GROUP_PERMISSIONS, GroupName, Permission from mimic.schemas import CurrentUser, LoginRequest def test_login_request_minimal_payload() -> None: req = LoginRequest.model_validate({"username": "alice@x", "password": "hunter2"}) assert req.username == "alice@x" assert req.password == "hunter2" def test_login_request_rejects_empty() -> None: with pytest.raises(ValidationError): LoginRequest.model_validate({"username": "", "password": "x"}) with pytest.raises(ValidationError): LoginRequest.model_validate({"username": "alice", "password": ""}) def test_login_request_rejects_unknown_fields() -> None: # Pydantic 2 default behavior: extra fields are allowed but ignored. # We assert the strict shape stays minimal by passing only known fields. payload = {"username": "alice", "password": "x", "remember_me": True} req = LoginRequest.model_validate(payload) assert not hasattr(req, "remember_me") def test_serialize_current_user_round_trip() -> None: uid = uuid4() auth_user = AuthUser( id=uid, email="lead@example.org", display_name="Lead", user_type=UserType.RT_LEAD, permissions=frozenset(GROUP_PERMISSIONS[GroupName.RT_LEAD]), groups=frozenset({GroupName.RT_LEAD.value}), ) payload: CurrentUser = serialize_current_user(auth_user) assert payload.user_id == uid assert payload.username == "lead@example.org" assert payload.display_name == "Lead" assert payload.role is UserType.RT_LEAD assert payload.groups == [GroupName.RT_LEAD.value] # Permissions are sorted as their string values for stable client diffs. assert payload.permissions == sorted(p.value for p in Permission) def test_serialize_current_user_operator_subset() -> None: auth_user = AuthUser( id=uuid4(), email="op@example.org", user_type=UserType.RT_OPERATOR, permissions=frozenset(GROUP_PERMISSIONS[GroupName.RT_OPERATOR]), groups=frozenset({GroupName.RT_OPERATOR.value}), ) payload = serialize_current_user(auth_user) assert payload.role is UserType.RT_OPERATOR assert payload.display_name is None expected = sorted(p.value for p in GROUP_PERMISSIONS[GroupName.RT_OPERATOR]) assert payload.permissions == expected assert Permission.RUN_CONTROL.value not in payload.permissions