"""Tests for physical product logic.""" from oto import response import pytest import sentry_sdk from ows_product_physical.constant import error from ows_product_physical.constant import field from ows_product_physical.logic import ownership from ows_product_physical.logic import product_physical from ows_product_physical.models import ows_carveouts from ows_product_physical.models import ows_marketing from ows_product_physical.models import ows_product from ows_product_physical.models import ows_project from ows_product_physical.models import persister from ows_product_physical import features @pytest.fixture(autouse=True) def mock_supply_chain_feature_flag(mocker): """Prevent Flask app-context access from feature flag checks in unit tests.""" mocker.patch.object( features, 'is_ccm_physical_supplychaininfo_oa_enabled', return_value=False ) @pytest.fixture() def pass_ownership_check(mocker): """Force ownership check to pass.""" mocker.patch.object( ownership, 'check_project_ownership', return_value=response.Response( status=200), autospec=True) @pytest.fixture() def mock_placeholder_upc(mocker): """Mock placeholder upc generation.""" placeholder_upc = 20000000000042 mocker.patch.object( ows_product, 'generate_placeholder_upc', return_value=response.Response(message=placeholder_upc), autospec=True) return placeholder_upc def test_after_create_happy_path(mocker, feature_engine): """Ensure that callbacks are called correctly after creating product.""" mocker.patch.object( product_physical, 'create_product', return_value=response.Response( status=201, message={'product_id': 123}), autospec=True) mocker.patch.object( ows_marketing, 'create_product_highlight', return_value=response.Response(status=200), autospec=True) product_physical.create( {field.PRODUCT_HIGHLIGHTS: 'a highlight'}, 'foo', 'bar') assert ows_marketing.create_product_highlight.called assert product_physical.create_product.called def test_after_create_marketing_fail( mocker, valid_post_product_data, feature_engine): """Ensure that callbacks are called correctly after creating product.""" mocker.patch.object( product_physical, 'create_product', return_value=response.Response( status=201, message={'product_id': 123}), autospec=True) mocker.patch.object( ows_marketing, 'create_product_highlight', side_effect=True, autospec=True) mocker.patch.object( sentry_sdk, 'capture_exception', autospec=True) product_physical.create( {field.PRODUCT_HIGHLIGHTS: 'a highlight'}, 'foo', 'bar') assert ows_marketing.create_product_highlight.called assert sentry_sdk.capture_exception.called assert product_physical.create_product.called def test_create_product_subaccount_id_zero( mocker, pass_ownership_check, mock_placeholder_upc): """Test create product.""" vendor_id = 1234 project_code = 'applesauce' data = {'project_id': 28} artist_id = 123 mocker.patch.object( ows_project, 'get_project_by_id', return_value=response.Response( message={ 'vendor_id': vendor_id, 'subaccount_id': 0, 'project_code': project_code, 'artist_id': artist_id},), autospec=True) mocker.patch.object(persister, 'create_product', autospec=True) product_physical.create(data, 'vendor', 564) persister.create_product.assert_called_once_with( data, vendor_id=vendor_id, subaccount_id=None, project_code=project_code, artist_id=artist_id) def test_create_product_unable_to_assign_display_upc( mocker, pass_ownership_check, mock_placeholder_upc): """Test unable to assign upc.""" mocker.patch.object( ows_product, 'is_user_entered_upc_available', return_value=True) vendor_id = 1234 project_code = 'applesauce' data = {'project_id': 28, 'assign_display_upc': True} data['display_upc'] = '565685854679' artist_id = 123 mocker.patch.object( ows_project, 'get_project_by_id', return_value=response.Response( message={ 'vendor_id': vendor_id, 'subaccount_id': 0, 'project_code': project_code, 'artist_id': artist_id}, ), autospec=True) mocker.patch.object(persister, 'create_product', autospec=True) create_response = product_physical.create(data) assert create_response.message == ( 'If assign_display_upc is set, display_upc must not be set.') assert not persister.create_product.called def test_create_product_fail_ownership( mocker, valid_account_info, valid_post_product_data, feature_engine): """Test create product fails on ownership check failure.""" data = valid_post_product_data error_response = response.create_error_response( status=error.FORBIDDEN_CODE, code=error.OWNERSHIP_ERROR, message="doesn't matter") mocker.patch.object(ownership, 'check_project_ownership', return_value=error_response) mocker.patch.object(persister, 'create_product') create_response = product_physical.create(data, *valid_account_info) assert not persister.create_product.called assert create_response is error_response def test_create_product_project_fetch_fail( mocker, valid_account_info, valid_post_product_data, pass_ownership_check, feature_engine): """Test create product fails on project fetch failure.""" data = valid_post_product_data mocker.patch.object( ows_project, 'get_project_by_id', return_value=response.create_error_response( code='whatever', message='whatever', status=404), autospec=True) mocker.patch.object(persister, 'create_product') create_response = product_physical.create(data, *valid_account_info) assert create_response.status == 404 assert ownership.check_project_ownership.called assert ows_project.get_project_by_id.called assert not persister.create_product.called def test_create_product_fail_upc_availability_check( mocker, valid_post_product_data, valid_account_info, pass_ownership_check): """Test create product fails on upc availability check failure.""" mocker.patch.object( ows_project, 'get_project_by_id', return_value=response.Response( message={ field.VENDOR_ID: 7123, field.SUBACCOUNT_ID: 555 }), autospec=True) user_entered_upc = valid_post_product_data.get(field.DISPLAY_UPC) valid_post_product_data[field.DISPLAY_UPC] = user_entered_upc mocker.patch.object(ows_product, 'is_user_entered_upc_available', return_value=False) mocker.patch.object(persister, 'create_product') mocker.patch.object( ows_product, 'generate_placeholder_upc', return_value=response.Response(message=20000000000042), autospec=True) data = valid_post_product_data create_response = product_physical.create(data, *valid_account_info) ows_product.is_user_entered_upc_available.assert_called_once_with( user_entered_upc, 7123, 555) assert create_response.status == 400 assert create_response.errors == { 'code': error.VALIDATION_ERROR, 'message': { field.DISPLAY_UPC: { 'validator': 'available', 'validator_value': True, 'message': 'UPC is not available.'}}} assert not persister.create_product.called def test_create_product_product_code_uniqueness_check_failure( mocker, valid_post_product_data, valid_post_header, valid_physical_releases, pass_ownership_check, feature_engine): """Test create product fails on upc availability check failure.""" mocker.patch.object( ows_project, 'get_project_by_id', return_value=response.Response( message={ field.VENDOR_ID: 7123, field.SUBACCOUNT_ID: 555 }), autospec=True) mocker.patch.object( ows_product, 'is_display_upc_available', return_value=True, autospec=True) mocker.patch.object( ows_product, 'generate_placeholder_upc', return_value=response.Response(message=20000000000042), autospec=True) mocker.patch.object( persister, 'get_physical_release_count_by_product_code', return_value=response.Response(message=1)) mocker.spy(persister, 'create_product') mocker.patch.object( product_physical, 'check_product_code_uniqueness', return_value=response.create_error_response( code='validation_error', message={ 'product_code': { 'message': 'Product code is in use', 'validator': 'used', 'validator_value': True}})) account_type = valid_post_header['Grass-Account-Type'] account_id = valid_post_header['Grass-Account-Id'] product_code = valid_physical_releases[0].get(field.PRODUCT_CODE) valid_post_product_data.update({'product_code': product_code}) data = valid_post_product_data create_response = product_physical.create(data, account_type, account_id) assert create_response.status == 400 assert create_response.errors == { 'code': error.VALIDATION_ERROR, 'message': { field.PRODUCT_CODE: { 'validator': 'used', 'validator_value': True, 'message': 'Product code is in use'}}} assert not persister.create_product.called @pytest.mark.parametrize( ( 'ownership_response,' 'project_response,' 'upc_available_response,' 'generate_upc_response,' 'uniqueness_response,' 'create_product_response,' 'carveins_response,' 'carveins_called,' 'supply_chain_metadata_response,' 'supply_chain_metadata_called,' 'expected_status_code' ), [ ( response.Response(), response.Response( message={ 'vendor_id': 666, 'subaccount_id': None, 'project_code': 'ABC123', 'artist_id': 999, } ), response.Response(), response.Response(message='20000000000042'), response.Response(), response.Response(message={'product_id': 444}), response.Response(), True, response.Response(), True, 200 ) ] ) def test_create_product( mocker, ownership_response, project_response, upc_available_response, generate_upc_response, uniqueness_response, create_product_response, carveins_response, carveins_called, supply_chain_metadata_response, supply_chain_metadata_called, expected_status_code, feature_engine, ): """Test product_physical.create_product().""" mocker.patch.object( ownership, 'check_project_ownership', return_value=ownership_response) mocker.patch.object( ows_project, 'get_project_by_id', return_value=project_response) mocker.patch.object( ows_product, 'is_user_entered_upc_available', return_value=upc_available_response) mocker.patch.object( ows_product, 'generate_placeholder_upc', return_value=generate_upc_response) mocker.patch.object( product_physical, 'check_product_code_uniqueness', return_value=uniqueness_response) mocker.patch.object( persister, 'create_product', return_value=create_product_response) carveins_setter = mocker.patch.object( ows_carveouts, 'set_carveins_for_physical_product', return_value=carveins_response) metadata_creator = mocker.patch.object( persister, 'create_product_physical_supply_chain_metadata', return_value=supply_chain_metadata_response) mocker.patch.object( features, 'is_ccm_physical_supplychaininfo_oa_enabled', return_value=True ) fields = {'project_id': 123} account_type = 'vendor' account_id = 7123 result = product_physical.create_product( fields, account_type=account_type, account_id=account_id) assert result.status == expected_status_code assert carveins_setter.called == carveins_called assert metadata_creator.called == supply_chain_metadata_called carveins_setter.assert_called_once_with(444, 666) metadata_creator.assert_called_once_with(fields, 444)