"""Test POST /v2/vendors.""" import time from typing import Any, Tuple import pytest import requests from tests.integration import config from tests.integration.utils import convert_to_grass_headers company_brand_ids = { 'altafonte': 10, 'awal': 5, 'knr': 1, 'sme': 4, 'theorchard': 2, 'hrs': 3, 'msk': 6, 'ab': 7, 'drm': 8, 'ma': 9, 'foundation': 11, } orchadmin_user_ids = { 'altafonte': 100075, 'awal': 100073, 'knr': 100074, 'sme': 100076, 'theorchard': 100077, 'hrs': 100077, 'msk': 100077, 'ab': 100077, 'drm': 100077, 'ma': 100077, 'foundation': 100076, } orchard_brands = ['theorchard', 'hrs', 'msk', 'ab', 'drm', 'ma'] sme_brands = ['foundation'] @pytest.fixture( scope='function', params=[ 'altafonte', 'awal', 'knr', 'sme', 'theorchard', 'hrs', 'msk', 'ab', 'drm', 'ma', 'foundation', ], ) def get_vendor_attrs_and_token(request) -> Tuple[dict[str, Any], str, int]: if request.param in orchard_brands: fixture_name_suffix = 'theorchard' elif request.param in sme_brands: fixture_name_suffix = 'sme' else: fixture_name_suffix = request.param bearer_token_fixture = f'bearer_token_contract_admin_{fixture_name_suffix}' bearer_token = request.getfixturevalue(bearer_token_fixture) return ( { 'name': f'ows-account-integration-test-{request.param}-{time.time()}', 'owner': 'odd', 'company_brand': request.param, 'company_brand_id': company_brand_ids[request.param], 'service_tier_uuid': '5f2bd4fc-df94-4f35-97d3-ef23f8573279', 'payment_currency': 'USD', }, bearer_token, orchadmin_user_ids[request.param], ) @pytest.fixture def get_created_vendor_payload( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> dict[str, Any]: vendor_attrs, _, orchadmin_user_id = get_vendor_attrs_and_token return { 'is_distributor': 'N', 'owner': vendor_attrs['owner'], 'name': vendor_attrs['name'], 'company': None, 'company_brand_id': vendor_attrs['company_brand_id'], 'contact_email': None, 'status': 'signed', 'is_owned': 'No', 'last_modified_by': orchadmin_user_id, 'migrated_to_abacus': True, } def test_v2_create_vendor( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], get_created_vendor_payload: dict[str, Any], ) -> None: """Test account admin can create a Vendor for the respective company_brand.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert all(item in actual.items() for item in get_created_vendor_payload.items()) def test_v2_create_vendor_fails_for_invalid_company_brand( bearer_token_contract_admin_awal: str, ) -> None: """Test account admin cannot create a Vendor for the invalid company_brand.""" headers = {'Authorization': f'Bearer {bearer_token_contract_admin_awal}'} vendor_attrs = { 'name': f'ows-account-integration-test-sme-{time.time()}', 'owner': 'odd', 'company_brand': 'randombrand', 'company_brand_id': 4, 'service_tier_uuid': '5f2bd4fc-df94-4f35-97d3-ef23f8573279', 'payment_currency': 'USD', } response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 400, response.text assert response.json() == { 'code': 'input_validation_error', 'message': { 'company_brand': [ 'Must be one of: ' + 'ab, altafonte, awal, drm, foundation, hrs, knr, ma, msk, sme, theorchard.' ] }, } @pytest.mark.parametrize( 'get_vendor_attrs_and_token', [ 'knr', ], indirect=True, ) def test_v2_create_vendor_fails_for_non_company_brand_admin( bearer_token_contract_admin_awal: str, get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test AWAL account admin cannot create a Vendor for the non-awal company_brand.""" headers = {'Authorization': f'Bearer {bearer_token_contract_admin_awal}'} vendor_attrs, _, _ = get_vendor_attrs_and_token response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 403, response.text assert response.json() == {'code': 'authorization_error', 'message': 'Forbidden'} def test_v2_create_vendor_fails_for_traditional_vendor_star( bearer_token_with_vendor_star: str, get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test an ows-permissions vendor star-configured identity cannot create a Vendor.""" headers = {'Authorization': f'Bearer {bearer_token_with_vendor_star}'} vendor_attrs, _, _ = get_vendor_attrs_and_token response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 403, response.text assert response.json() == {'code': 'authorization_error', 'message': 'Forbidden'} def test_v2_create_vendor_fails_for_non_vendor_star( bearer_token_without_vendor_star: str, get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test an ows-permissions without-vendor-star identity cannot create a Vendor.""" headers = {'Authorization': f'Bearer {bearer_token_without_vendor_star}'} vendor_attrs, _, _ = get_vendor_attrs_and_token response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 403, response.text assert response.json() == {'code': 'authorization_error', 'message': 'Forbidden'} def test_v2_create_vendor_is_distributor_is_true( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], get_created_vendor_payload: dict[str, Any], ) -> None: """Test if is_distributor is set to True, it is stored as 'Y'.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['is_distributor'] = True headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert actual['is_distributor'] == 'Y' assert actual['label_identifier'] == 'D3' def test_v2_create_vendor_is_distributor_is_not_present( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], get_created_vendor_payload: dict[str, Any], ) -> None: """Test if is_distributor is not set, it is stored as 'N'.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token # remove is_distributor key from vendor_attrs vendor_attrs.pop('is_distributor', None) headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert actual['is_distributor'] == 'N' assert actual['label_identifier'] is None def test_v2_create_vendor_is_distributor_is_false( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], get_created_vendor_payload: dict[str, Any], ) -> None: """Test if is_distributor is set to False, it is stored as 'N'.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['is_distributor'] = False headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert actual['is_distributor'] == 'N' def test_v2_create_vendor_with_country( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test vendor can be created with a valid ISO alpha-3 country code.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['country'] = 'USA' headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text def test_v2_create_vendor_with_invalid_country_format( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test country code that is not exactly 3 chars fails schema validation.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['country'] = 'US' headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 400, response.text assert response.json() == { 'code': 'input_validation_error', 'message': {'country': ['Length must be 3.']}, } def test_v2_create_vendor_with_primary_genre( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test vendor can be created with a primary_genre integer.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['primary_genre'] = 1 headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text def test_v2_create_vendor_with_label_summary( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test vendor can be created with a label_summary text field.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['label_summary'] = 'Test summary' headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text @pytest.mark.parametrize( 'get_vendor_attrs_and_token', ['theorchard'], indirect=True, ) def test_v2_create_vendor_with_staff_fields( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test vendor can be created with all four internal staff fields.""" vendor_attrs, bearer_token, orchadmin_user_id = get_vendor_attrs_and_token vendor_attrs['assigned_to'] = orchadmin_user_id vendor_attrs['assigned_reviewer'] = orchadmin_user_id vendor_attrs['quarterback_label_manager'] = orchadmin_user_id vendor_attrs['wel_email_sender'] = orchadmin_user_id headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert actual['assigned_to'] == orchadmin_user_id assert actual['assigned_reviewer'] == orchadmin_user_id assert actual['quarterback_label_manager'] == orchadmin_user_id assert actual['wel_email_sender'] == orchadmin_user_id @pytest.mark.parametrize( 'get_vendor_attrs_and_token', ['theorchard'], indirect=True, ) def test_v2_create_vendor_staff_fields_omitted_defaults_to_null( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test staff fields are null when not provided.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text actual = response.json() assert actual['assigned_to'] is None assert actual['assigned_reviewer'] is None assert actual['quarterback_label_manager'] is None assert actual['wel_email_sender'] is None def _assert_vendor_closers(vendor_uuid: str, expected_closers: list[int]) -> None: """Fetch a vendor's closers via the dataloader endpoint and assert they match.""" dataloader_headers = convert_to_grass_headers( # ows-account-integration-test@sonymusic-pde.com, who has an OA profile identity_id='40210960-c0e6-47c7-bb49-71f2373260dd', profile_type='OrchAdminProfile', profile_id=95101924951, ) response = requests.post( f'{config.QA_BASE_URL}/vendors/closers/dataloader', json={'vendor_uuids': [vendor_uuid]}, headers=dataloader_headers, ) assert response.status_code == 200, response.text closers_data = response.json() assert len(closers_data) == 1 assert closers_data[0]['uuid'] == vendor_uuid assert closers_data[0]['closers'] == expected_closers def test_v2_create_awal_vendor_creates_default_closer( bearer_token_contract_admin_awal: str, ) -> None: """Test creating an AWAL vendor automatically creates default closer 2912.""" vendor_attrs = { 'name': f'ows-account-integration-test-awal-{time.time()}', 'owner': 'awal', 'company_brand': 'awal', 'service_tier_uuid': '5f2bd4fc-df94-4f35-97d3-ef23f8573279', 'payment_currency': 'USD', } headers = {'Authorization': f'Bearer {bearer_token_contract_admin_awal}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text vendor_uuid = response.json()['vendor_uuid'] _assert_vendor_closers(vendor_uuid, [2912]) @pytest.mark.parametrize( 'get_vendor_attrs_and_token', ['theorchard'], indirect=True, ) def test_v2_create_vendor_with_closers( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test a vendor created with an explicit closers list has those closers applied.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['closers'] = [11, 68] headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text vendor_uuid = response.json()['vendor_uuid'] _assert_vendor_closers(vendor_uuid, [11, 68]) def test_v2_create_awal_vendor_with_closers_overrides_default( bearer_token_contract_admin_awal: str, ) -> None: """Test explicit closers on an AWAL vendor override the default 2912 closer.""" vendor_attrs = { 'name': f'ows-account-integration-test-awal-{time.time()}', 'owner': 'awal', 'company_brand': 'awal', 'service_tier_uuid': '5f2bd4fc-df94-4f35-97d3-ef23f8573279', 'payment_currency': 'USD', 'closers': [11, 68], } headers = {'Authorization': f'Bearer {bearer_token_contract_admin_awal}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text vendor_uuid = response.json()['vendor_uuid'] _assert_vendor_closers(vendor_uuid, [11, 68]) @pytest.mark.parametrize( 'get_vendor_attrs_and_token', ['theorchard'], indirect=True, ) def test_v2_create_vendor_with_product_manager( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], ) -> None: """Test vendor can be created with product_manager; mapping row is written.""" vendor_attrs, bearer_token, orchadmin_user_id = get_vendor_attrs_and_token vendor_attrs['product_manager'] = orchadmin_user_id headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 200, response.text vendor_id = response.json()['vendor_id'] # Read-after-write: GET /vendor/ hits a read replica; retry while it catches up. for _ in range(10): get_response = requests.get(f'{config.QA_BASE_URL}/vendor/{vendor_id}', headers=headers) if get_response.status_code == 200: break time.sleep(0.5) assert get_response.status_code == 200, get_response.text assert get_response.json()['product_manager'] == orchadmin_user_id @pytest.mark.parametrize( 'is_distributor', [ 'N', 'Y', ], ) def test_v2_create_vendor_is_distributor_is_invalid( get_vendor_attrs_and_token: Tuple[dict[str, Any], str, int], get_created_vendor_payload: dict[str, Any], is_distributor: Any, ) -> None: """Test if is_distributor is set to invalid values, it returns an input validation error.""" vendor_attrs, bearer_token, _ = get_vendor_attrs_and_token vendor_attrs['is_distributor'] = is_distributor headers = {'Authorization': f'Bearer {bearer_token}'} response = requests.post(f'{config.QA_BASE_URL}/v2/vendors', json=vendor_attrs, headers=headers) assert response.status_code == 400, response.text assert response.json() == { 'code': 'input_validation_error', 'message': {'is_distributor': ['Not a valid boolean.']}, }