"""Unit Tests for contract_term_schedule handlers.""" from unittest import mock from unittest.mock import MagicMock, patch import pytest from flask import testing as flask_testing from owsresponse import response from abacus_contract.tests.utils.factories import ContractTermFactory from core.config import Config STANDALONE_HEADERS = { 'Orchard-Requestor-Service': 'graphql-abacus', 'Orchard-Profile-Type': 'ContentProfile', 'Orchard-Profile-Id': '1234', 'Orchard-Roles': 'manage_nr_ownership', 'Orchard-Identity-Id': '1234', } @mock.patch('abacus_contract.blueprints.contract_term_schedule.logic') def test_get_contract_term_schedules_by_term_id(mock_logic, fixture_client): """Test to get a list of contract term schedules by contract_term_id.""" mock_contract_term = ContractTermFactory.create() contract_term_id = mock_contract_term.contract_term_id mock_logic.get_contract_term_schedules_by_term_id.return_value = response.Response( message='ok', status=200 ) res = fixture_client.get( f'/contract-term/{contract_term_id}/contract-term-schedules' ) assert res.status_code == 200 mock_logic.get_contract_term_schedules_by_term_id.assert_called_once_with( contract_term_id ) @pytest.mark.parametrize( [ 'standalone_check_result', 'get_account_id_result', 'pdp_auth_result', 'expected_status', ], [ pytest.param(True, None, None, 200, id='Standalone check pass'), pytest.param(False, None, None, 403, id='Account id not found'), pytest.param(False, 199, False, 403, id='PDP auth fail'), pytest.param(True, 199, True, 200, id='PDP auth pass'), ], ) @mock.patch('abacus_contract.blueprints.contract_term_schedule.logic') @mock.patch('abacus_contract.blueprints.contract_term_schedule.authorization') @mock.patch('abacus_contract.blueprints.contract_term_schedule.contract_term_logic') @mock.patch('abacus_contract.blueprints.contract_term_schedule.flask_request') def test_get_contract_term_schedules_by_term_id_authorization( mock_flask_request: mock.MagicMock, mock_contract_term_logic: mock.MagicMock, mock_authorization: mock.MagicMock, mock_logic: mock.MagicMock, standalone_check_result: bool, get_account_id_result: int | None, pdp_auth_result: bool | None, expected_status: int, fixture_client: flask_testing.FlaskClient, ) -> None: """Test authorization for getting contract term schedules.""" contract_term_id = 33 mock_flask_request.verify_rules_access_standalone.return_value = ( standalone_check_result ) mock_contract_term_logic.get_account_id_by_contract_term_id.return_value = ( get_account_id_result ) mock_authorization.pdp_authorize_many_accounts.return_value = pdp_auth_result mock_logic.get_contract_term_schedules_by_term_id.return_value = response.Response() res = fixture_client.get( f'/contract-term/{contract_term_id}/contract-term-schedules' ) assert res.status_code == expected_status mock_flask_request.verify_rules_access_standalone.assert_called_once() if not standalone_check_result: mock_contract_term_logic.get_account_id_by_contract_term_id.assert_called_once_with( contract_term_id ) if get_account_id_result is not None: mock_authorization.pdp_authorize_many_accounts.assert_called_once_with( [199] ) @patch('abacus_contract.blueprints.contract_term_schedule.contract_term_logic') def test_get_contract_term_schedules_by_term_ids_dataloader_over_cap( mock_contract_term_logic: MagicMock, fixture_client: flask_testing.FlaskClient, ) -> None: """Over-cap batches are rejected with a 400 before any lookup happens.""" ids = list(range(Config.OWS_BATCH_LIMIT + 1)) res = fixture_client.post('/contract-term-schedules/dataloader', json=ids) assert res.status_code == 400 mock_contract_term_logic.get_account_ids_by_contract_term_ids.assert_not_called() @patch('abacus_contract.utils.dataloader.ows_client') @patch('abacus_contract.utils.dataloader.permissions_authorize_many_accounts') @patch('abacus_contract.blueprints.contract_term_schedule.logic') @patch('abacus_contract.utils.dataloader.authorization') @patch('abacus_contract.blueprints.contract_term_schedule.contract_term_logic') def test_get_contract_term_schedules_by_term_ids_dataloader( mock_contract_term_logic: MagicMock, mock_authorization: MagicMock, mock_logic: MagicMock, mock_permissions_authorize_many_accounts: MagicMock, mock_ows_client: MagicMock, fixture_client: flask_testing.FlaskClient, ) -> None: """One-to-many happy path: records are grouped per term id in request order. Only authorized ids reach fetch_records; an id that does not resolve to an account surfaces as data: null. """ # Term 3 does not resolve to an account and must be dropped from fetch. mock_contract_term_logic.get_account_ids_by_contract_term_ids.return_value = { 1: 9, 2: 9, } mock_permissions_authorize_many_accounts.return_value = True mock_logic.get_contract_term_schedule_records_by_term_ids.return_value = [ {'contract_term_schedule_id': 100, 'contract_term_id': 1, 'schedule_id': 10}, {'contract_term_schedule_id': 101, 'contract_term_id': 1, 'schedule_id': 11}, {'contract_term_schedule_id': 102, 'contract_term_id': 2, 'schedule_id': 12}, ] res = fixture_client.post( '/contract-term-schedules/dataloader', json=[1, 2, 3], headers=STANDALONE_HEADERS, ) assert res.status_code == 200 assert res.json == [ { 'data': [ { 'contract_term_schedule_id': 100, 'contract_term_id': 1, 'schedule_id': 10, }, { 'contract_term_schedule_id': 101, 'contract_term_id': 1, 'schedule_id': 11, }, ] }, { 'data': [ { 'contract_term_schedule_id': 102, 'contract_term_id': 2, 'schedule_id': 12, } ] }, {'data': None}, ] # Only the authorized (account-resolved) ids reach fetch_records. mock_logic.get_contract_term_schedule_records_by_term_ids.assert_called_once_with( [1, 2] )