"""Identity field dictionary — completeness, truthfulness, RBAC.""" from __future__ import annotations from app.auth.schemas import MyProfilePatchRequest from app.identity.field_catalog import build_field_catalog from app.identity.person_models import IdentityOrgNode, IdentityPerson def test_catalog_covers_every_column_except_excluded(): cat = build_field_catalog() user_keys = {f["key"] for f in cat["user_fields"]} model_cols = {c.name for c in IdentityPerson.__table__.columns} # Every column shows up except the deliberately-excluded secrets/plumbing. assert user_keys == model_cols - {"hashed_password", "deleted_at"} assert "hashed_password" not in user_keys # never surface the secret dept_keys = {f["key"] for f in cat["department_fields"]} assert dept_keys == {c.name for c in IdentityOrgNode.__table__.columns} - {"deleted_at"} def test_user_editable_matches_self_service_patch_truth(): cat = build_field_catalog() editable = {f["key"] for f in cat["user_fields"] if f["user_editable"]} # The editable set MUST equal the live self-service PATCH body fields — # not a hand-maintained second copy. assert editable == set(MyProfilePatchRequest.model_fields) assert editable == {"preferred_language"} def test_field_shapes_and_types(): cat = build_field_catalog() by_key = {f["key"]: f for f in cat["user_fields"]} assert by_key["birthday"]["source"] == "hr_managed" assert by_key["birthday"]["type"] == "date" assert by_key["last_login_at"]["source"] == "sso" assert by_key["last_login_at"]["type"] == "datetime" assert by_key["id"]["type"] == "uuid" assert by_key["mobile_phone"]["source"] == "hris" # every row has the full user shape for f in cat["user_fields"]: assert set(f) == { "key", "label_key", "type", "source", "console_visible", "self_visible", "user_editable", } async def test_endpoint_rbac(client, admin_headers, employee_headers): ok = await client.get("/api/v1/identity/field-catalog", headers=admin_headers) assert ok.status_code == 200, ok.text data = ok.json()["data"] assert len(data["user_fields"]) > 30 assert len(data["department_fields"]) > 5 forbidden = await client.get( "/api/v1/identity/field-catalog", headers=employee_headers, ) assert forbidden.status_code == 403