"""Unittests for product status change endpoint.""" import json from unittest.mock import MagicMock from flexmock import flexmock from owsrequest import request from owsrequest.test_utils import MockOwsResponse from owsresponse import response import pytest from product_digital import features from product_digital.connectors import mysql from product_digital.constants import error from product_digital.constants import header from product_digital.constants import services from product_digital.logic import product from product_digital.models import ows_product from product_digital.models import ows_product_workflow from product_digital.models import project from product_digital.models import release from product_digital.models.release import Release from product_digital.models.release_status import ReleaseStatus from tests.factories.release import ReleaseFactory from tests.testutils import db @pytest.fixture def product_id(): """Hard coded product id for tests.""" return 12345 @pytest.fixture def validation_get_response(): """Return a mock GET response with no validation errors.""" return MagicMock( message={'validations': {}} ) @pytest.fixture def validation_get_response_with_errors(): """Return a mock GET response with validation errors.""" return MagicMock( message={ 'validations': { 'product_basics': { 'valid': False } } } ) @pytest.fixture def project_get_response(): """Return a mock GET response call to get project.""" return MagicMock( message={ 'vendor_id': '0' } ) @pytest.fixture def ows_project_get_response(): """Return a mock get call to ows_project.""" project_response = MagicMock(status_code=200) project_response.json = MagicMock(return_value={'vendor_id': '0'}) return project_response @pytest.fixture def release_approval_create_response(): """Return response from creating a release approval.""" release_approval_create_message = { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in'} release_approval_response = MagicMock(status_code=200) release_approval_response.json = MagicMock( return_value=release_approval_create_message) return release_approval_response @db.test_schema def test_put_submit_product( client, mocker, product_id, project_get_response, validation_get_response, valid_headers, request_engine ): """Test that you can make a PUT request.""" mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response ) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response, ) request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') workflow_submission_payload = { 'account_id': '', 'account_type': '', 'error_correction_flag': True, 'identity_id': '' } workflow_submission_response = MockOwsResponse(status_code=200, json={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) (flexmock(request) .should_receive('post') .with_args( services.OWS_PRODUCT_WORKFLOW, '/product/{}/submission'.format(product_id), json=workflow_submission_payload) .and_return(workflow_submission_response)) (flexmock(request) .should_receive('post') .with_args( services.OWS_PRODUCT_REVIEW, f'/product/{product_id}/submission') .and_return(MockOwsResponse(status_code=200, json='ineligible'))) release = ReleaseFactory.build(release_id=product_id) db.seed_models(release) put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers ) response_body = json.loads(put_response.data.decode()) assert put_response.status_code == 200 # Note the release status remains changed to transfer_to_content. assert response_body == { 'artist_id': release.artist_id, 'c_line': '2017 Trump', 'delivered_version': None, 'description': None, 'distribution_format_id': 1, 'format': 'Full Length', 'genre_id': release.genre_id, 'imprint': 'DIG for Gold Records', 'is_error_correction_and_action_required': False, 'manufacturer_upc': None, 'meta_language': 'eng', 'p_line': '2020 PLINE', 'preorder_date': None, 'previewable': 'no', 'product_artists': [], 'product_code': release.product_code, 'product_id': 12345, 'product_name': release.release_name, 'product_type_id': None, 'project_id': release.project_id, 'release_approval_status': 'checked_in', 'release_date': None, 'release_status': 'transfer_to_content', 'sale_start_date': None, 'special_instructions': 'Listen to something else instead', 'subaccount_id': None, 'subgenre_id': None, 'upc': None, 'vendor_catalog_number': None, 'version': 'Extremely Explicit Version', 'not_for_distribution': 'N', } @db.test_schema def test_put_submit_product_changes_status_to_transfer_to_content( client, mocker, product_id, project_get_response, validation_get_response, valid_headers, request_engine ): """Test that the status is changed on success.""" request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response ) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response ) mocker.patch.object( ows_product_workflow, 'product_submission', return_value=MagicMock(message={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) ) release = ReleaseFactory.build(release_id=product_id) db.seed_models(release) client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers ) with mysql.db_session() as session: release = session.query(Release).get(product_id) assert release.release_status == 'transfer_to_content' @db.test_schema def test_put_submit_product_creates_release_status_record( client, mocker, product_id, project_get_response, validation_get_response, valid_headers, request_engine ): """Test a release status record is created on success.""" request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response ) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response ) mocker.patch.object( ows_product_workflow, 'product_submission', return_value=MagicMock(message={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) ) release = ReleaseFactory.build(release_id=product_id) db.seed_models(release) client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers ) with mysql.db_session() as session: release_status_record = session \ .query(ReleaseStatus) \ .filter(ReleaseStatus.release_id == product_id) \ .first() assert release_status_record is not None @db.test_schema def test_put_submit_product_creates_release_approval_queue_record( client, request_engine, mocker, orchard_user_id, product_id, project_get_response, validation_get_response, valid_headers, ): """Test a release approval queue record is created on success.""" request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response) release = ReleaseFactory.build(release_id=product_id) db.seed_models(release) workflow_submission_payload = { 'account_id': '', 'account_type': '', 'error_correction_flag': True, 'identity_id': '' } workflow_submission_response = MagicMock(status_code=200) workflow_submission_response.json = MagicMock(return_value={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) (flexmock(request) .should_receive('post') .with_args( services.OWS_PRODUCT_WORKFLOW, '/product/{}/submission'.format(product_id), json=workflow_submission_payload) .and_return(workflow_submission_response)) valid_headers[header.ORCHARD_USER_ID] = orchard_user_id put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers) assert put_response.status_code == 200 @db.test_schema def test_put_product_status_when_resubmitting_after_rejection( client, orchard_user_id, ows_project_get_response, product_id, valid_headers, validation_get_response, mocker ): """Test a release approval queue record is created on success.""" mocker.patch.object( ows_product_workflow, 'is_product_checked_out_by_another_user', return_value=False) (flexmock(request).should_receive('get').with_args( services.OWS_PROJECT_MANAGER, '/project/1').and_return( ows_project_get_response).once()) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) (flexmock(product).should_receive('validate_audio_product').and_return( validation_get_response).once()) workflow_submission_payload = { 'account_id': '', 'account_type': '', 'error_correction_flag': True, 'identity_id': '' } workflow_submission_response = MagicMock(status_code=200) workflow_submission_response.json = MagicMock(return_value={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) (flexmock(request) .should_receive('post') .with_args( services.OWS_PRODUCT_WORKFLOW, '/product/{}/submission'.format(product_id), json=workflow_submission_payload) .and_return(workflow_submission_response)) (flexmock(request) .should_receive('get') .with_args( services.OWS_PRODUCT_REVIEW, f'/review-queue/product/{product_id}/eligibility-check') .and_return(MockOwsResponse(status_code=200, json='ineligible'))) release = ReleaseFactory.build(release_id=product_id, project_id=1) db.seed_models(release) valid_headers[header.ORCHARD_USER_ID] = orchard_user_id put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers) response_body = json.loads(put_response.data.decode()) assert put_response.status_code == 200 assert response_body.get('release_status') == 'transfer_to_content' assert response_body.get('rejections') is None @db.test_schema def test_put_submit_product_returns_400_if_product_is_incomplete( client, mocker, product_id, validation_get_response_with_errors, valid_headers): """Test that the status is not changed if the product is incomplete.""" mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response_with_errors) release = ReleaseFactory.build(release_id=product_id) db.seed_models(release) put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers) assert put_response.status_code == 400 @db.test_schema def test_put_submit_product_if_product_is_in_content( client, mocker, product_id, project_get_response, validation_get_response, valid_headers, request_engine ): """Test the status is not updated when product is not label_processing.""" request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response ) mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response, ) mocker.patch.object( ows_product_workflow, 'product_submission', return_value=MagicMock(message={ 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) ) release = ReleaseFactory.build( release_id=product_id, release_status='in_content' ) db.seed_models(release) put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers ) response_body = json.loads(put_response.data.decode()) assert put_response.status_code == 200 # Note the release status remains as in_content. assert response_body == { 'artist_id': release.artist_id, 'c_line': '2017 Trump', 'description': None, 'delivered_version': None, 'distribution_format_id': 1, 'format': 'Full Length', 'genre_id': release.genre_id, 'imprint': 'DIG for Gold Records', 'is_error_correction_and_action_required': False, 'manufacturer_upc': None, 'meta_language': 'eng', 'p_line': '2020 PLINE', 'preorder_date': None, 'previewable': 'no', 'product_artists': [], 'product_code': release.product_code, 'product_id': 12345, 'product_name': release.release_name, 'product_type_id': None, 'project_id': release.project_id, 'release_date': None, 'release_status': 'transfer_to_content', 'sale_start_date': None, 'special_instructions': 'Listen to something else instead', 'subaccount_id': None, 'subgenre_id': None, 'upc': None, 'vendor_catalog_number': None, 'version': 'Extremely Explicit Version', 'release_approval_status': 'checked_in', 'not_for_distribution': 'N', } # The database status should not changed. with mysql.db_session() as session: release = session.query(Release).get(product_id) assert release.release_status == 'in_content' @db.test_schema def test_put_submit_product_if_product_is_error_correction( client, mocker, orchard_user_id, product_id, project_get_response, valid_headers, validation_get_response, request_engine ): """Test status is transfer_to_content when the e.c. is submitted.""" mocker.patch.object( project, 'get_project_by_id', return_value=project_get_response ) request_engine[services.OWS_PRODUCT_REVIEW].add_spec( 'GET', '/review-queue/product/12345/eligibility-check', 'ineligible') mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response, ) correction = { 'release_correction_id': 1, 'status': 'submitted', 'items': [ { 'release_correction_detail_id': 1, 'table_name': 'releases', 'field_name': 'primary', 'key_id': 288212, 'key_value': 'HEY HEY HEY' }, { 'release_correction_detail_id': 2, 'table_name': 'releases', 'field_name': 'primary', 'key_id': 288213, 'key_value': 'EYH EYH EYH' }, { 'release_correction_detail_id': 3, 'table_name': 'releases', 'field_name': 'primary', 'key_id': 288214, 'key_value': 'HYE HYE HYE' } ] } mocker.patch.object( ows_product_workflow, 'product_submission', return_value=MagicMock(message={ 'latest_correction': correction, 'release_approval': { 'release_approval_id': 1, 'release_id': product_id, 'status': 'checked_in' } }) ) release = ReleaseFactory.build( release_id=product_id, release_status='in_content' ) db.seed_models(release) valid_headers[header.ORCHARD_USER_ID] = orchard_user_id put_response = client.put( '/product/audio/{}/status'.format(product_id), headers=valid_headers ) response_body = json.loads(put_response.data.decode()) assert put_response.status_code == 200 # Note the release status remains as in_content. assert response_body == { 'artist_id': release.artist_id, 'c_line': '2017 Trump', 'corrections': correction, 'delivered_version': None, 'description': None, 'distribution_format_id': 1, 'format': 'Full Length', 'genre_id': release.genre_id, 'imprint': 'DIG for Gold Records', 'is_error_correction_and_action_required': False, 'manufacturer_upc': None, 'meta_language': 'eng', 'p_line': '2020 PLINE', 'preorder_date': None, 'previewable': 'no', 'product_artists': [], 'product_code': release.product_code, 'product_id': 12345, 'product_name': release.release_name, 'product_type_id': None, 'project_id': release.project_id, 'release_date': None, 'release_status': 'transfer_to_content', 'sale_start_date': None, 'special_instructions': 'Listen to something else instead', 'subaccount_id': None, 'subgenre_id': None, 'upc': None, 'vendor_catalog_number': None, 'version': 'Extremely Explicit Version', 'release_approval_status': 'checked_in', 'not_for_distribution': 'N', } # The database status should not changed. with mysql.db_session() as session: release = session.query(Release).get(product_id) assert release.release_status == 'in_content' @db.test_schema def test_put_submit_product_returns_400_if_product_is_already_submitted( client, mocker, valid_headers, validation_get_response): """Test that status 400 is returned when product is already submitted.""" mocker.patch.object( ows_product, 'get_product', return_value=response.Response({'vendor_id': 123}) ) mocker.patch.object( features, 'is_feature_flag_enabled_for_vendor', return_value=True, ) mocker.patch.object( product, 'validate_audio_product', return_value=validation_get_response, ) mocker.patch.object( release, 'get_release_instance', return_value=MagicMock(release_status='transfer_to_content') ) put_response = client.put( '/product/audio/{}/status'.format(1234), headers=valid_headers) assert put_response.status_code == 400 response_body = put_response.data.decode() assert response_body == error.ERROR_MESSAGE_PRODUCT_ALREADY_SUBMITTED