"""Unit tests for the mobile RoleSwitcher perspective resolver. ``resolve_perspective_role_names`` is the single source of truth shared by the apps catalog AND the action inbox (the surface where a prior bug leaked admin/auditor items into the employee perspective). Pure function, no DB — guards the narrowing contract, especially that it only ever NARROWS and fails CLOSED on a perspective the caller doesn't hold. """ from app.mobile.perspective import resolve_perspective_role_names def test_no_as_role_returns_full_set(): assert resolve_perspective_role_names(["global_admin", "employee"], None) == [ "global_admin", "employee", ] def test_unknown_as_role_falls_back_to_full(): # Typos / removed roles must not surprise the client with an empty grid. assert resolve_perspective_role_names(["global_admin"], "wat") == ["global_admin"] def test_employee_perspective_collapses_to_empty(): # Strips every DB-backed admin role; manager-scope is org-tree-driven. assert resolve_perspective_role_names(["global_admin", "employee"], "employee") == [] def test_manager_perspective_collapses_to_empty(): assert resolve_perspective_role_names(["global_admin"], "manager") == [] def test_held_role_narrows_to_that_role(): assert resolve_perspective_role_names(["global_admin", "auditor"], "auditor") == [ "auditor" ] def test_known_role_not_held_fails_closed(): # The fix: a perspective the caller doesn't hold collapses to [] (no # widen, no fall-open to the full set). An admin asking for the auditor # perspective they don't hold sees the empty/self surface, never the # full admin set — and certainly never auditor items. assert resolve_perspective_role_names(["global_admin"], "auditor") == [] assert resolve_perspective_role_names(["employee"], "global_admin") == [] def test_never_widens(): # The cardinal invariant: the result is always a subset of the caller's # roles, OR empty. Never introduces a role the caller lacks. held = ["asset_admin", "employee"] for perspective in [None, "wat", "employee", "manager", "asset_admin", "global_admin", "auditor"]: out = resolve_perspective_role_names(held, perspective) assert set(out).issubset(set(held)), (perspective, out)