"""Test Roles Utils.""" from typing import Any, Dict, List from unittest.mock import MagicMock import pytest from pdp.fastapi.schemas.identity import RoleListValidator from pdp.utils import roles @pytest.mark.parametrize( "description, old_roles, new_roles, expected", [ ( "Lists are not different.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "zookeeper"}, {"role": "beekeeper"}], False, ), ( "Lists are not different, although they are not the same order.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "beekeeper"}, {"role": "zookeeper"}], False, ), ("Two empty lists are still equal", [], [], False), ( "Lists are different when the second one includes janitor.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "zookeeper"}, {"role": "beekeeper"}, {"role": "janitor"}], True, ), ( "Lists are different when the first one is empty.", [], [{"role": "zookeeper"}, {"role": "beekeeper"}], True, ), ( "Lists are different when the second one is empty.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [], True, ), ( "Lists are different when the old roles has more attributes.", [{"role": "beekeeper", "bee_types": ["killer"]}], [{"role": "beekeeper"}], True, ), ( "Lists are different when the roles have different attribute values.", [{"role": "beekeeper", "bee_types": ["killer"]}], [{"role": "beekeeper", "bee_types": []}], True, ), ( "Lists are different when the roles have different attribute value items.", [{"role": "beekeeper", "bee_types": ["killer"]}], [{"role": "beekeeper", "bee_types": ["honey"]}], True, ), ( "Lists are not different when all the attributes are the same.", [{"role": "beekeeper", "bee_types": ["honey"]}], [{"role": "beekeeper", "bee_types": ["honey"]}], False, ), ], ) def test_is_new_list_different( description: str, old_roles: List[Dict[str, Any]], new_roles: List[Dict[str, Any]], expected: bool, ) -> None: """Test is_new_list_different method.""" result = roles.is_new_list_different( RoleListValidator.validate_python(old_roles), RoleListValidator.validate_python(new_roles), ) assert result is expected, description @pytest.mark.parametrize( "description, existing_roles, roles_to_detach, expected", [ ( "every role should be removed.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "zookeeper"}, {"role": "beekeeper"}], [], ), ( "an empty list is still an empty list.", [], [{"role": "zookeeper"}, {"role": "beekeeper"}], [], ), ( "removing an empty list from an empty list is still an empty list.", [], [], [], ), ( "zookeeper should be removed.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "zookeeper"}], [{"role": "beekeeper"}], ), ( "teacher role is ignored.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "teacher"}], [{"role": "zookeeper"}, {"role": "beekeeper"}], ), ( "no roles are removed when roles_to_detach is empty.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [], [{"role": "zookeeper"}, {"role": "beekeeper"}], ), ( "role should be removed when the existing role has different attributes.", [{"role": "zookeeper", "type": "large mammals"}, {"role": "beekeeper"}], [{"role": "zookeeper"}], [{"role": "beekeeper"}], ), ( "role should be removed when the detach role has different attributes.", [{"role": "zookeeper"}, {"role": "beekeeper"}], [{"role": "zookeeper", "type": "large mammals"}], [{"role": "beekeeper"}], ), ], ) def test_detach_roles( description: str, existing_roles: List[Dict[str, Any]], roles_to_detach: List[Dict[str, Any]], expected: List[Dict[str, Any]], ) -> None: """Test detach_roles method.""" result = roles.detach_roles( RoleListValidator.validate_python(existing_roles), RoleListValidator.validate_python(roles_to_detach), ) assert result == RoleListValidator.validate_python(expected), description @pytest.mark.parametrize( "description, existing_roles, roles_to_attach, expected", [ ( "roles_to_attach are added to empty list", [], [{"role": "lion tamer"}, {"role": "zookeeper", "animal_type": "big cats"}], [{"role": "lion tamer"}, {"role": "zookeeper", "animal_type": "big cats"}], ), ( "two empty lists makes an empty list", [], [], [], ), ], ) def test_attach_roles( description: str, existing_roles: List[Any], roles_to_attach: List[Dict[str, str]], expected: List[Dict[str, str]], ) -> None: """Test attach_roles.""" result = roles.attach_roles( RoleListValidator.validate_python(existing_roles), RoleListValidator.validate_python(roles_to_attach), ) assert result == RoleListValidator.validate_python(expected), description @pytest.mark.parametrize( "description, roles_to_attach, expected", [ ( "role to attach should replace existing role.", [ {"role": "lion tamer"}, {"role": "zookeeper", "animal_type": "big cats"}, ], [ {"role": "zookeeper", "animal_type": "big cats"}, {"role": "beekeeper"}, {"role": "lion tamer"}, ], ), ( "duplicate roles to attach should use the last item in list.", [ {"role": "zookeeper"}, {"role": "beekeeper"}, {"role": "beekeeper"}, {"role": "beekeeper", "attribute": "something"}, {"role": "beekeeper", "attribute": "other thing"}, ], [{"role": "zookeeper"}, {"role": "beekeeper", "attribute": "other thing"}], ), ( "empty roles_to_attach should not change existing_roles", [], [{"role": "zookeeper"}, {"role": "beekeeper"}], ), ( "order of roles is used to determine tie-breaker.", [ {"role": "zookeeper"}, {"role": "beekeeper", "dont": "keep"}, {"role": "beekeeper", "keep": "this"}, ], [{"role": "zookeeper"}, {"role": "beekeeper", "keep": "this"}], ), ], ) def test_attach_roles_starting_with_mock_existing_roles( description: str, roles_to_attach: List[Dict[str, str]], expected: List[Dict[str, str]], mock_existing_roles: MagicMock, ) -> None: """Test attach_roles starting from mock_existing_roles.""" result = roles.attach_roles( RoleListValidator.validate_python(mock_existing_roles), RoleListValidator.validate_python(roles_to_attach), ) assert result == RoleListValidator.validate_python(expected), description