"""Test ows-permissions connector and types.""" import logging from typing import Any, Dict, Optional import httpx import pytest from owsclient import AsyncOwsClient from owsclient.test import OwsClientMock from pdp.connectors import ows_permissions def test_ows_permission_resource_alias() -> None: """Test OwsPermissionResource field aliases.""" valid_object = ows_permissions.OwsPermissionsResource.model_validate( {"type": "Vendor", "id": 123, "uuid": "uuid1"} ) assert valid_object.resource_type == "Vendor" assert not hasattr(valid_object, "type") assert valid_object.resource_id == 123 assert not hasattr(valid_object, "id") @pytest.mark.parametrize( "payload, expect_exception, expected, description", [ ({}, True, None, "Empty object not allowed"), ({"id": "hello", "uuid": "uuid1"}, True, None, "Missing type"), ({"type": "Vendor", "uuid": "uuid1"}, True, None, "Missing id"), ({"id": "hello", "type": "Vendor"}, True, None, "Missing uuid"), ( {"type": "Vendor", "id": "hello", "uuid": "uuid1"}, False, {"type": "Vendor", "id": "hello", "uuid": "uuid1"}, "Should be valid resource", ), ( {"type": "Vendor", "id": 123, "uuid": "uuid1"}, False, {"type": "Vendor", "id": 123, "uuid": "uuid1"}, "An integer resource id is preserved as integer", ), ], ) def test_ows_permissions_resource_model_validate( payload: Any, expect_exception: bool, expected: Dict[str, Any], description: str, ) -> None: """Test OwsPermissionResource.""" if expect_exception: with pytest.raises(Exception): ows_permissions.OwsPermissionsResource.model_validate(payload) else: actual = ows_permissions.OwsPermissionsResource.model_validate(payload) assert actual == ows_permissions.OwsPermissionsResource(**expected), description @pytest.mark.parametrize( "resource_type, expected, description", [ ("Vendor", True, "Vendor is supported"), ("Subaccount", True, "Subaccount is supported"), ("CompanyBrand", True, "CompanyBrand is supported"), ("veNDOR", True, "Capitalization does not matter for Vendor"), ("sUBACCounT", True, "Capitalization does not matter for Subaccount"), ("companyBRAND", True, "Capitalization does not matter for CompanyBrand"), ("LabelParticipant", False, "LabelParticipant is not supported"), ("Collaborator", False, "Collaborator is not supported"), ], ) def test_ows_permissions_resource_is_supported_resource_type( resource_type: str, expected: bool, description: str ) -> None: """Test OwsPermissionsResource helper function is_supported_resource_type.""" resource = ows_permissions.OwsPermissionsResource( type=resource_type, id="mock_id", uuid="mock_uuid" ) assert resource.is_supported_resource_type() == expected, description @pytest.mark.parametrize( "resource_type, expect_exception, expected, description", [ ("Vendor", False, "account", "Vendor is mapped to account"), ("Subaccount", False, "subaccount", "Subaccount is mapped to subaccount"), ("CompanyBrand", False, "company_brand", "CompanyBrand is supported"), ("veNDOR", False, "account", "Capitalization does not matter for Vendor"), ( "sUBACCounT", False, "subaccount", "Capitalization does not matter for Subaccount", ), ( "companyBRAND", False, "company_brand", "Capitalization does not matter for CompanyBrand", ), ("LabelParticipant", True, None, "LabelParticipant is not supported"), ("Collaborator", True, None, "Collaborator is not supported"), ], ) def test_ows_permissions_resource_get_tenant_type( resource_type: str, expect_exception: bool, expected: Optional[str], description: str, ) -> None: """Test OwsPermissionsResource helper function get_tenant_type.""" resource = ows_permissions.OwsPermissionsResource( type=resource_type, id="mock_id", uuid="mock_uuid" ) if expect_exception: with pytest.raises(Exception): resource.get_tenant_type() else: assert resource.get_tenant_type() == expected, description def test_ows_pagination_alias() -> None: """Test OwsPagination field aliases.""" valid_object = ows_permissions.OwsPagination.model_validate( {"type": "page", "total_records": 1} ) assert valid_object.pagination_type == "page" assert not hasattr(valid_object, "type") @pytest.mark.parametrize( "payload, expect_exception, expected, description", [ ({}, True, None, "Empty object not allowed"), ({"total_records": 1}, True, None, "Missing type"), ({"type": "page"}, True, None, "Missing total_records"), ( {"type": "page", "total_records": "what"}, True, None, "total_records should be integer", ), ( {"type": "page", "total_records": 1}, False, {"type": "page", "total_records": 1}, "Should be valid resource", ), ], ) def test_ows_pagination_model_validate( payload: Any, expect_exception: bool, expected: Dict[str, Any], description: str, ) -> None: """Test OwsPagination.""" if expect_exception: with pytest.raises(Exception): ows_permissions.OwsPagination.model_validate(payload) else: actual = ows_permissions.OwsPagination.model_validate(payload) assert actual == ows_permissions.OwsPagination(**expected), description @pytest.mark.parametrize( "total_records, item_count, offset, expected_next_offset", [ (0, 0, 0, 0), (1, 1, 0, 0), (1, 1, 2, 0), (11, 1, 0, 1), (11, 3, 0, 3), (11, 3, 3, 6), (10, 4, 3, 7), (10, 5, 5, 0), (11, 5, 6, 0), ], ) async def test_ows_pagination_get_next_offset( total_records: int, item_count: int, offset: int, expected_next_offset: int ) -> None: """Test OwsPagination get_next_offset helper.""" pagination = ows_permissions.OwsPagination( type="standard", total_records=total_records ) actual_next_offset = pagination.get_next_offset(item_count, offset) assert actual_next_offset == expected_next_offset @pytest.mark.parametrize( "payload, expect_exception, expected, description", [ ({}, True, None, "Empty object not allowed"), ({"items": []}, True, None, "Missing pagination"), ( {"pagination": {"type": "page", "total_records": 1}}, True, None, "Missing items", ), ( {"items": [], "pagination": {"type": "page", "total_records": 1}}, False, {"items": [], "pagination": {"type": "page", "total_records": 1}}, "Empty list of items allowed", ), ( { "items": [{"type": "Vendor", "id": 123, "uuid": "uuid1"}], "pagination": {"type": "page", "total_records": 1}, }, False, { "items": [{"type": "Vendor", "id": 123, "uuid": "uuid1"}], "pagination": {"type": "page", "total_records": 1}, }, "Valid response for user with one adminable resource", ), ], ) def test_adminable_resources_response_model_validate( payload: Any, expect_exception: bool, expected: Dict[str, Any], description: str, ) -> None: """Test AdminableResourcesResponse.""" if expect_exception: with pytest.raises(Exception): ows_permissions.AdminableResourcesResponse.model_validate(payload) else: actual = ows_permissions.AdminableResourcesResponse.model_validate(payload) assert actual == ows_permissions.AdminableResourcesResponse(**expected), ( description ) @pytest.fixture def ows_permissions_client( mock_async_ows_client: AsyncOwsClient, ) -> ows_permissions.OwsPermissionsClient: """Return fixture for OwsPermissionsClient.""" return ows_permissions.OwsPermissionsClient(async_ows_client=mock_async_ows_client) async def test_get_my_adminable_resources( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_my_adminable_resources call.""" ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 0, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 7123, "uuid": "573d0372-7f2f-48a6-8deb-c9a6558f9549", } ], "pagination": {"type": "classico", "total_records": 1}, }, ) ) result = await ows_permissions_client.get_my_adminable_resources() assert result assert result == ows_permissions.AdminableResourcesResponse( items=[ ows_permissions.OwsPermissionsResource( type="Vendor", id=7123, uuid="573d0372-7f2f-48a6-8deb-c9a6558f9549" ) ], pagination=ows_permissions.OwsPagination(type="classico", total_records=1), ) async def test_get_my_adminable_resources_custom_offset_limit( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_my_adminable_resources call with offset/limit.""" offset = 2 limit = 100 ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": offset, "limit": limit}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 7123, "uuid": "573d0372-7f2f-48a6-8deb-c9a6558f9549", } ], "pagination": {"type": "classico", "total_records": 1}, }, ) ) result = await ows_permissions_client.get_my_adminable_resources( offset=offset, limit=limit ) assert result assert result == ows_permissions.AdminableResourcesResponse( items=[ ows_permissions.OwsPermissionsResource( type="Vendor", id=7123, uuid="573d0372-7f2f-48a6-8deb-c9a6558f9549" ) ], pagination=ows_permissions.OwsPagination(type="classico", total_records=1), ) async def test_unsuccessful_get_my_adminable_resources( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_my_adminable_resources call with 400 response raises exception.""" ows_client_mock.get("ows-permissions", path="/identity/admin/resources/all/").mock( return_value=httpx.Response( 400, json={"code": "400", "message": "bad"}, ) ) with pytest.raises(Exception): await ows_permissions_client.get_my_adminable_resources() async def test_get_my_adminable_resources_connection_timeout( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, caplog: Any, ) -> None: """Test get_my_adminable_resources call with ConnectTimeout logs error.""" ows_client_mock.get("ows-permissions", path="/identity/admin/resources/all/").mock( side_effect=httpx.ConnectTimeout(message="mock error") ) response = await ows_permissions_client.get_my_adminable_resources() assert response.items == [] with caplog.at_level(logging.ERROR): assert "Connection timeout while connecting to ows-permissions." in caplog.text async def test_get_all_my_adminable_resources( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_all_my_adminable_resources generator returns first and only page.""" ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 0, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 7123, "uuid": "573d0372-7f2f-48a6-8deb-c9a6558f9549", } ], "pagination": {"type": "standard", "total_records": 1}, }, ) ) result = await ows_permissions_client.collect_get_my_adminable_resources() assert result == [ ows_permissions.OwsPermissionsResource( type="Vendor", id=7123, uuid="573d0372-7f2f-48a6-8deb-c9a6558f9549" ) ] async def test_get_all_my_adminable_resources_multiple_pages( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_all_my_adminable_resources generator yields multiple pages.""" ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 0, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 7123, "uuid": "573d0372-7f2f-48a6-8deb-c9a6558f9549", } ], "pagination": {"type": "standard", "total_records": 3}, }, ) ) ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 1, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 22351, "uuid": "1531ba9a-6cf9-4813-af70-081b59fa7579", } ], "pagination": {"type": "standard", "total_records": 3}, }, ) ) ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 2, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 34921, "uuid": "dbbf10f9-8049-498d-8976-66a52082bafb", } ], "pagination": {"type": "standard", "total_records": 3}, }, ) ) result = await ows_permissions_client.collect_get_my_adminable_resources() assert result == [ ows_permissions.OwsPermissionsResource( type="Vendor", id=7123, uuid="573d0372-7f2f-48a6-8deb-c9a6558f9549" ), ows_permissions.OwsPermissionsResource( type="Vendor", id=22351, uuid="1531ba9a-6cf9-4813-af70-081b59fa7579" ), ows_permissions.OwsPermissionsResource( type="Vendor", id=34921, uuid="dbbf10f9-8049-498d-8976-66a52082bafb" ), ] async def test_get_all_my_adminable_resources_on_error( ows_permissions_client: ows_permissions.OwsPermissionsClient, ows_client_mock: OwsClientMock, ) -> None: """Test get_all_my_adminable_resources generator yields multiple pages.""" ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 0, "limit": 200}, ).mock( return_value=httpx.Response( 200, json={ "items": [ { "type": "Vendor", "id": 7123, "uuid": "573d0372-7f2f-48a6-8deb-c9a6558f9549", } ], "pagination": {"type": "standard", "total_records": 3}, }, ) ) ows_client_mock.get( "ows-permissions", path="/identity/admin/resources/all/", params={"offset": 1, "limit": 200}, ).mock( return_value=httpx.Response( 400, json={"code": "400", "message": "bad"}, ) ) with pytest.raises(Exception): await ows_permissions_client.collect_get_my_adminable_resources()