"""Contract Exclusion handler tests.""" from unittest.mock import MagicMock, patch import pytest from flask.testing import FlaskClient from owsresponse import response @patch('abacus_contract.blueprints.contract_exclusion.logic') def test_create_contract_exclusions(mock_logic, fixture_client): """Test POST /contract//exclusions/.""" contract_id = 1 mock_logic.create_contract_exclusions.return_value = response.Response( message='ok', status=201 ) json_body = {'countries': ['AFG', 'GNQ'], 'stores': ['12']} res = fixture_client.post(f'/contract/{contract_id}/exclusions/', json=json_body) assert res.status_code == 201 mock_logic.create_contract_exclusions.assert_called_once_with( contract_id, json_body ) @patch('abacus_contract.blueprints.contract_exclusion.logic') def test_update_contract_exclusions(mock_logic, fixture_client): """Test PUT /contract//exclusions/.""" contract_id = 1 mock_logic.update_contract_exclusions.return_value = response.Response( message='ok', status=200 ) json_body = {'countries': ['AFG', 'GNQ'], 'stores': ['12']} res = fixture_client.put(f'/contract/{contract_id}/exclusions/', json=json_body) assert res.status_code == 200 mock_logic.update_contract_exclusions.assert_called_once_with( contract_id, json_body ) @pytest.mark.parametrize( ( 'profile_type', 'profile_role', 'pdp_authorize_return', 'permissions_authorize_return', 'expected_status', 'get_account_id_result', ), [ pytest.param( 'ContentProfile', 'manage_nr_ownership', None, True, 200, 1, id='Standalone check OK, Permissions check OK', ), pytest.param( 'ContentProfile', 'manage_nr_ownership', None, False, 403, 1, id='Standalone check OK, Permissions check not OK', ), pytest.param( 'Account360Profile', 'account360', True, True, 200, 1, id='Standalone check not OK, PDP check OK', ), pytest.param( 'Account360Profile', 'account360', False, None, 403, 1, id='Standalone check not OK, PDP check not OK', ), pytest.param( 'Account360Profile', 'account360', False, None, 404, None, id='Account not found', ), ], ) @patch('abacus_contract.blueprints.contract_exclusion.ows_client') @patch( 'abacus_contract.blueprints.contract_exclusion.permissions_authorize_many_accounts' ) @patch('abacus_contract.blueprints.contract_exclusion.pdp_authorize_many_accounts') @patch('abacus_contract.blueprints.contract_exclusion.logic') @patch('abacus_contract.blueprints.contract_exclusion.contract_logic') def test_get_exclusions_by_contract( mock_contract_logic: MagicMock, mock_contract_exclusion_logic: MagicMock, mock_pdp_authorize_many_accounts: MagicMock, mock_permissions_authorize_many_accounts: MagicMock, mock_ows_client: MagicMock, fixture_client: FlaskClient, profile_type: str, profile_role: str, pdp_authorize_return: bool, permissions_authorize_return: bool, expected_status: int, get_account_id_result: int | None, ) -> None: """Test GET /contract//exclusions/.""" contract_id = 1 mock_contract_logic.get_account_id_by_contract_id.return_value = ( get_account_id_result ) mock_pdp_authorize_many_accounts.return_value = pdp_authorize_return mock_permissions_authorize_many_accounts.return_value = permissions_authorize_return mock_contract_exclusion_logic.get_exclusions_by_contract.return_value = ( response.Response(message='ok', status=200) ) result = fixture_client.get( f'/contract/{contract_id}/exclusions/', headers={ 'Orchard-Requestor-Service': 'graphql-abacus', 'Orchard-Profile-Type': profile_type, 'Orchard-Profile-Id': '1234', 'Orchard-Roles': profile_role, 'Orchard-Identity-Id': '1234', }, ) assert result.status_code == expected_status mock_contract_logic.get_account_id_by_contract_id.assert_called_once_with( contract_id ) # Check if the PDP check was called when the standalone check failed if profile_type == 'Account360Profile': if get_account_id_result: mock_pdp_authorize_many_accounts.assert_called_once_with( [get_account_id_result] ) else: mock_pdp_authorize_many_accounts.assert_not_called() else: mock_pdp_authorize_many_accounts.assert_not_called() if pdp_authorize_return is not False: if get_account_id_result: mock_permissions_authorize_many_accounts.assert_called_once_with( mock_ows_client, profile_type, '1234', [get_account_id_result] ) else: mock_permissions_authorize_many_accounts.assert_not_called() else: mock_permissions_authorize_many_accounts.assert_not_called() if expected_status == 200: ( mock_contract_exclusion_logic.get_exclusions_by_contract.assert_called_once_with( contract_id ) ) else: mock_contract_exclusion_logic.get_exclusions_by_contract.assert_not_called() @patch('abacus_contract.utils.dataloader.ows_client') @patch('abacus_contract.utils.dataloader.permissions_authorize_many_accounts') @patch('abacus_contract.blueprints.contract_exclusion.logic') @patch('abacus_contract.utils.dataloader.authorization') @patch('abacus_contract.blueprints.contract_exclusion.contract_logic') def test_get_exclusions_by_contract_ids_dataloader( mock_contract_logic: MagicMock, mock_authorization: MagicMock, mock_logic: MagicMock, mock_permissions_authorize_many_accounts: MagicMock, mock_ows_client: MagicMock, fixture_client: FlaskClient, ) -> None: """Happy path: one exclusion record per contract_id in request order. An id that does not resolve to an account (3) is dropped from the fetch and surfaces as ``data: None`` (one-to-one shaping). """ contract_ids = [1, 2, 3] mock_contract_logic.get_account_id_map_by_contract_ids.return_value = {1: 9, 2: 10} mock_authorization.pdp_authorize_many_accounts.return_value = True mock_permissions_authorize_many_accounts.return_value = True mock_logic.get_exclusion_records_by_contract_ids.return_value = [ {'contract_exclusion_id': 100, 'contract_id': 1}, {'contract_exclusion_id': 200, 'contract_id': 2}, ] res = fixture_client.post( '/contract-exclusions/dataloader', json=contract_ids, headers={ 'Orchard-Requestor-Service': 'graphql-abacus', 'Orchard-Profile-Type': 'ContentProfile', 'Orchard-Profile-Id': '1234', 'Orchard-Roles': 'manage_nr_ownership', 'Orchard-Identity-Id': '1234', }, ) assert res.status_code == 200 mock_contract_logic.get_account_id_map_by_contract_ids.assert_called_once_with( contract_ids ) # Only the account-resolved (and authorized) ids reach the fetch. mock_logic.get_exclusion_records_by_contract_ids.assert_called_once_with([1, 2]) # One record per contract_id in request order; the unresolved id 3 is data: null. assert res.get_json() == [ {'data': {'contract_exclusion_id': 100, 'contract_id': 1}}, {'data': {'contract_exclusion_id': 200, 'contract_id': 2}}, {'data': None}, ]