"""Test PATCH /v2/vendor//internal-staff.""" import time from typing import Any import pytest import requests from tests.integration import config TRUST_SAFETY_OA_USER_ID = 3783 @pytest.fixture() def theorchard_vendor_for_internal_staff() -> dict[str, Any]: return { 'is_distributor': False, 'name': f'ows-account integration-test internal-staff {time.time()}', 'owner': 'odd', 'company_brand': 'theorchard', 'service_tier_uuid': '5f2bd4fc-df94-4f35-97d3-ef23f8573279', 'payment_currency': 'USD', } @pytest.fixture() def created_vendor_uuid( bearer_token_contract_admin_theorchard: str, theorchard_vendor_for_internal_staff: dict[str, Any], ) -> str: """Create a vendor and return its UUID.""" headers = {'Authorization': f'Bearer {bearer_token_contract_admin_theorchard}'} resp = requests.post( f'{config.QA_BASE_URL}/v2/vendors', json=theorchard_vendor_for_internal_staff, headers=headers, ) assert resp.status_code == 200, resp.text return resp.json()['vendor_uuid'] def test_update_all_internal_staff_fields( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """PATCH /v2/vendor//internal-staff updates all fields.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} payload = { 'assigned_to': TRUST_SAFETY_OA_USER_ID, 'assigned_reviewer': TRUST_SAFETY_OA_USER_ID, 'quarterback_label_manager': TRUST_SAFETY_OA_USER_ID, 'wel_email_sender': TRUST_SAFETY_OA_USER_ID, 'product_manager': TRUST_SAFETY_OA_USER_ID, } resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json=payload, headers=headers, ) assert resp.status_code == 200, resp.text assert resp.json()['vendor_uuid'] == created_vendor_uuid def test_update_partial_fields( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """PATCH with only quarterback_label_manager and assigned_reviewer.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} payload = { 'quarterback_label_manager': TRUST_SAFETY_OA_USER_ID, 'assigned_reviewer': TRUST_SAFETY_OA_USER_ID, } resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json=payload, headers=headers, ) assert resp.status_code == 200, resp.text assert resp.json()['vendor_uuid'] == created_vendor_uuid def test_update_single_field( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """PATCH with only assigned_to.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} payload = {'assigned_to': TRUST_SAFETY_OA_USER_ID} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json=payload, headers=headers, ) assert resp.status_code == 200, resp.text assert resp.json()['vendor_uuid'] == created_vendor_uuid def test_update_product_manager_only( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """PATCH with only product_manager upserts the mapping row.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} payload = {'product_manager': TRUST_SAFETY_OA_USER_ID} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json=payload, headers=headers, ) assert resp.status_code == 200, resp.text assert resp.json()['vendor_uuid'] == created_vendor_uuid def test_update_empty_payload_rejected( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """PATCH with no staff fields returns 400.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json={}, headers=headers, ) assert resp.status_code == 400, resp.text assert resp.json() == { 'code': 'input_validation_error', 'message': { '_schema': [ 'At least one of assigned_to, assigned_reviewer, ' 'quarterback_label_manager, wel_email_sender, ' 'product_manager must be provided.' ], }, } @pytest.mark.parametrize( 'field', ['assigned_to', 'assigned_reviewer', 'quarterback_label_manager', 'wel_email_sender'], ) def test_unset_nullable_staff_field( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, field: str, ) -> None: """Sending null for a nullable FK clears it in the DB.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} set_resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json={field: TRUST_SAFETY_OA_USER_ID}, headers=headers, ) assert set_resp.status_code == 200, set_resp.text vendor_id = set_resp.json()['vendor_id'] unset_resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json={field: None}, headers=headers, ) assert unset_resp.status_code == 200, unset_resp.text assert unset_resp.json()['vendor_uuid'] == created_vendor_uuid get_resp = requests.get( f'{config.QA_BASE_URL}/vendor/{vendor_id}', headers=headers, ) assert get_resp.status_code == 200, get_resp.text assert get_resp.json()[field] is None def test_unset_product_manager_rejected( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, ) -> None: """product_manager cannot be unset via this endpoint (DB join table only).""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json={'product_manager': None}, headers=headers, ) assert resp.status_code == 400, resp.text assert resp.json() == { 'code': 'input_validation_error', 'message': {'product_manager': ['Field may not be null.']}, } @pytest.mark.parametrize( 'payload,expected_error', [ pytest.param( {'assigned_to': 'not_an_int'}, { 'code': 'input_validation_error', 'message': {'assigned_to': ['Not a valid integer.']}, }, id='string-value-rejected', ), pytest.param( {'unknown_field': 123}, {'code': 'input_validation_error', 'message': {'unknown_field': ['Unknown field.']}}, id='unknown-field-rejected', ), ], ) def test_update_internal_staff_validation_errors( bearer_token_account_admin_theorchard: str, created_vendor_uuid: str, payload: dict[str, Any], expected_error: dict[str, Any], ) -> None: """PATCH /v2/vendor//internal-staff rejects invalid payloads.""" headers = {'Authorization': f'Bearer {bearer_token_account_admin_theorchard}'} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json=payload, headers=headers, ) assert resp.status_code == 400, resp.text assert resp.json() == expected_error def test_update_internal_staff_forbidden_contract_admin( bearer_token_contract_admin_sme: str, created_vendor_uuid: str, ) -> None: """Contract admin for a different brand cannot update internal staff.""" headers = {'Authorization': f'Bearer {bearer_token_contract_admin_sme}'} resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/{created_vendor_uuid}/internal-staff', json={'assigned_to': TRUST_SAFETY_OA_USER_ID}, headers=headers, ) assert resp.status_code == 403, resp.text assert resp.json() == {'code': 'authorization_error', 'message': 'Forbidden'} def test_update_internal_staff_forbidden_no_auth() -> None: """Request with no auth token gets 403.""" resp = requests.patch( f'{config.QA_BASE_URL}/v2/vendor/fff741c2-6def-4493-bfdf-c2bcb1128e02/internal-staff', json={'assigned_to': TRUST_SAFETY_OA_USER_ID}, ) assert resp.status_code == 401, resp.text