"""Contract Term Condition functional tests.""" from abacus_contract.tests.utils.factories import ( AccountContractFactory, ContractFactory, ContractTermConditionFactory, ContractTermFactory, ) from core.config import Config def test_create_contract_term_conditions_success(fixture_client): """POST /contract-term//conditions.""" contract_term = ContractTermFactory.create() json_body = [ { 'priority': 1, 'term_rate': 80.0, 'conditions': { 'countries': ['USA'], 'stores': ['some-store-1', 'some-another'], 'transaction_types': ['type-1', 'type-1-1'], }, 'contract_term_condition_name': 'test condition name 1', }, { 'priority': 2, 'term_rate': 60.0, 'conditions': { 'countries': ['UK'], 'stores': ['store-1', 'store-2'], 'transaction_types': ['type-1-2'], }, 'contract_term_condition_name': 'test condition name 2', }, { 'priority': 3, 'term_rate': 70.0, 'commission': 30.0, 'conditions': { 'countries': ['AUS'], 'stores': ['store-3', 'store-4'], 'transaction_types': ['type-1-2'], }, }, { 'priority': 4, 'commission': 46.40, 'conditions': { 'countries': ['CAN'], 'stores': ['store-5', 'store-6'], 'transaction_types': ['type-1-2'], }, }, ] result = fixture_client.post( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 201 assert result.json == [ { 'conditions': { 'transaction_types': ['type-1', 'type-1-1'], 'stores': ['some-store-1', 'some-another'], 'countries': ['USA'], }, 'contract_term_condition_id': 1, 'term_rate': '80.00', 'commission': '20.00', 'priority': 1, 'contract_term_id': 1, 'contract_term_condition_name': 'test condition name 1', }, { 'conditions': { 'transaction_types': ['type-1-2'], 'stores': ['store-1', 'store-2'], 'countries': ['UK'], }, 'contract_term_condition_id': 2, 'term_rate': '60.00', 'commission': '40.00', 'priority': 2, 'contract_term_id': 1, 'contract_term_condition_name': 'test condition name 2', }, { 'conditions': { 'transaction_types': ['type-1-2'], 'stores': ['store-3', 'store-4'], 'countries': ['AUS'], }, 'contract_term_condition_id': 3, 'term_rate': '70.00', 'commission': '30.00', 'priority': 3, 'contract_term_id': 1, 'contract_term_condition_name': None, }, { 'conditions': { 'transaction_types': ['type-1-2'], 'stores': ['store-5', 'store-6'], 'countries': ['CAN'], }, 'contract_term_condition_id': 4, 'term_rate': '53.60', 'commission': '46.40', 'priority': 4, 'contract_term_id': 1, 'contract_term_condition_name': None, }, ] def test_create_contract_term_conditions_invalid_total(fixture_client): """Test creating contract term conditions. when sum of commission and term_rate is not equal to 100. """ contract_term = ContractTermFactory.create() json_body = [ { 'priority': 1, 'term_rate': 100.0, 'commission': 20.0, 'conditions': { 'countries': ['USA'], 'stores': ['some-store-1', 'some-another'], 'transaction_types': ['type-1', 'type-1-1'], }, } ] result = fixture_client.post( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['message']['0'] == { '_schema': ['The sum of commissions and term_rate should be 100.'] } def test_create_contract_term_conditions_missing_fields(fixture_client): """Test creating contract term conditions. when commission and term_rate fields are missing in the POST request body. """ contract_term = ContractTermFactory.create() json_body = [ { 'priority': 1, 'conditions': { 'countries': ['USA'], 'stores': ['some-store-1', 'some-another'], 'transaction_types': ['type-1', 'type-1-1'], }, } ] result = fixture_client.post( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['message']['0'] == { '_schema': ['Both the term rate and commission cannot be blank.'] } def test_create_contract_term_conditions_invalid_commission(fixture_client): """Test creating contract term conditions. when commission value is not within the range of 0 to 100. """ contract_term = ContractTermFactory.create() json_body = [ { 'priority': 1, 'commission': 120, 'conditions': { 'countries': ['USA'], 'stores': ['some-store-1', 'some-another'], 'transaction_types': ['type-1', 'type-1-1'], }, } ] result = fixture_client.post( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['message']['0'] == { 'commission': [ 'Must be greater than or equal to 0.0 and less than or equal to 100.0.' ] } def test_get_conditions_by_contract_term(fixture_client, create_mock_account): """Test updating one or more contract_term_conditions.""" contract = ContractFactory.create() AccountContractFactory.create(contract=contract) contract_term = ContractTermFactory.create(contract=contract) contract_term_conditions = ContractTermConditionFactory.create_batch( 3, contract_term=contract_term ) result = fixture_client.get( f'/contract-term/{contract_term.contract_term_id}/conditions', ) assert result.status_code == 200 assert len(result.json) == len(contract_term_conditions) def test_get_conditions_by_term_ids_dataloader(fixture_client, create_mock_account): """POST /contract-term-conditions/dataloader batches conditions by contract_term_id.""" contract_1 = ContractFactory.create() AccountContractFactory.create(account_id=1, contract=contract_1) contract_term_1 = ContractTermFactory.create(contract=contract_1) contract_term_1_conditions = ContractTermConditionFactory.create_batch( 2, contract_term=contract_term_1 ) contract_2 = ContractFactory.create() AccountContractFactory.create(account_id=2, contract=contract_2) contract_term_no_conditions = ContractTermFactory.create(contract=contract_2) missing_contract_term_id = 999999 result = fixture_client.post( '/contract-term-conditions/dataloader', json=[ contract_term_1.contract_term_id, contract_term_no_conditions.contract_term_id, missing_contract_term_id, ], ) assert result.status_code == 200 # Ordered array: one entry per requested id, in requested order. assert len(result.json) == 3 assert len(result.json[0]['data']) == len(contract_term_1_conditions) assert all( condition['contract_term_id'] == contract_term_1.contract_term_id for condition in result.json[0]['data'] ) assert result.json[1]['data'] is None assert result.json[2]['data'] is None def test_get_conditions_by_term_ids_dataloader_unresolved_term_not_leaked( fixture_client, create_mock_account ): """A term with real conditions but no account_contract must not leak them. Regression test for an auth bypass: get_account_ids_by_contract_term_ids only resolves terms that have an account_contract, so those terms were excluded from authorization but their conditions were still fetched and returned. An account-less term's conditions must never reach the response, even though they exist in the database. """ contract = ContractFactory.create() AccountContractFactory.create(account_id=1, contract=contract) contract_term_authorized = ContractTermFactory.create(contract=contract) ContractTermConditionFactory.create_batch(2, contract_term=contract_term_authorized) contract_term_unresolved = ContractTermFactory.create() ContractTermConditionFactory.create_batch(2, contract_term=contract_term_unresolved) result = fixture_client.post( '/contract-term-conditions/dataloader', json=[ contract_term_authorized.contract_term_id, contract_term_unresolved.contract_term_id, ], ) assert result.status_code == 200 # Authorized term's conditions come back; the account-less term is {'data': None} # even though it has real conditions in the database. assert len(result.json[0]['data']) == 2 assert result.json[1]['data'] is None def test_get_conditions_by_term_ids_dataloader_over_cap(fixture_client): """POST /contract-term-conditions/dataloader rejects batches over OWS_BATCH_LIMIT.""" ids = list(range(1, Config.OWS_BATCH_LIMIT + 2)) result = fixture_client.post('/contract-term-conditions/dataloader', json=ids) assert result.status_code == 400 assert result.json['code'] == 'bad_request' def test_get_conditions_by_term_ids_dataloader_missing_body(fixture_client): """POST /contract-term-conditions/dataloader requires a non-empty ids body.""" result = fixture_client.post('/contract-term-conditions/dataloader', json=[]) assert result.status_code == 400 def test_soft_delete_contract_term_conditions_success(fixture_client): """Test soft deleting one or more contract_term_conditions.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] contract_term_condition_ids = [ condition.contract_term_condition_id for condition in contract_term_conditions ] result = fixture_client.put( '/contract-term-conditions/soft-delete', json=contract_term_condition_ids[:2] ) assert result.status_code == 200 for contract_term_condition in contract_term_conditions[:2]: assert contract_term_condition.deleted_at assert contract_term_condition.deleted_by def test_soft_delete_contract_term_conditions_reprioritizes_conditions(fixture_client): """Soft delete reprioritizes remaining contract term conditions.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] contract_term_condition_ids = [ condition.contract_term_condition_id for condition in contract_term_conditions ] result = fixture_client.put( '/contract-term-conditions/soft-delete', json=contract_term_condition_ids[:1] ) assert result.status_code == 200 assert contract_term_conditions[1].priority == 1 assert contract_term_conditions[2].priority == 2 def test_soft_delete_contract_term_condition_error(fixture_client): """Soft deleting returns error if result is no active contract term conditions.""" contract_term_condition = ContractTermConditionFactory.create() res = fixture_client.put( '/contract-term-conditions/soft-delete', json=[contract_term_condition.contract_term_condition_id], ) assert res.status_code == 400 assert not contract_term_condition.deleted_at assert not contract_term_condition.deleted_by def test_update_contract_term_conditions_success(fixture_client): """Test updating one or more contract_term_conditions.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] json_body = [ { 'contract_term_condition_id': condition.contract_term_condition_id, 'conditions': condition.conditions, 'priority': condition.priority, 'term_rate': 20.20, } for condition in contract_term_conditions ] result = fixture_client.put( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 200 assert len(result.json) == len(json_body) for condition in contract_term_conditions: assert str(condition.term_rate) == '20.20' assert str(condition.commission) == '79.80' def test_update_contract_term_conditions_for_commision(fixture_client): """Test to update commission field.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] json_body = [ { 'contract_term_condition_id': condition.contract_term_condition_id, 'commission': 13.09, } for condition in contract_term_conditions ] result = fixture_client.put( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 200 assert len(result.json) == len(json_body) for condition in contract_term_conditions: assert str(condition.term_rate) == '86.91' assert str(condition.commission) == '13.09' def test_update_contract_term_conditions_invalid_total(fixture_client): """Test updating commission and term_rate fields when total is not 100.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] json_body = [ { 'contract_term_condition_id': condition.contract_term_condition_id, 'commission': 80, 'term_rate': 30, } for condition in contract_term_conditions ] result = fixture_client.put( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['message']['0'] == { '_schema': ['The sum of commissions and term_rate should be 100.'] } def test_update_contract_term_conditions_invalid_term_rate(fixture_client): """Test updating term_rate field when value is not within range of 0 to 100.""" contract_term = ContractTermFactory.create() contract_term_conditions = [ ContractTermConditionFactory.create( contract_term=contract_term, priority=priority ) for priority in [1, 2, 3] ] json_body = [ { 'contract_term_condition_id': condition.contract_term_condition_id, 'term_rate': 130, } for condition in contract_term_conditions ] result = fixture_client.put( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['message']['0'] == { 'term_rate': [ 'Must be greater than or equal to 0.0 and less than or equal to 100.0.' ] } def test_update_contract_term_conditions_invalid_condition_id(fixture_client): """Updating one or more contract_term_conditions fails with invalid condition.""" contract_term = ContractTermFactory.create() contract_term_conditions = ContractTermConditionFactory.create_batch( 2, contract_term=contract_term ) json_body = [ { 'contract_term_condition_id': condition.contract_term_condition_id + 2, 'conditions': condition.conditions, 'priority': condition.priority, 'term_rate': 20.20, } for condition in contract_term_conditions ] result = fixture_client.put( f'/contract-term/{contract_term.contract_term_id}/conditions', json=json_body ) assert result.status_code == 400 assert result.json['code'] == 'error'