"""Unit tests for product workflow logic.""" import copy from unittest import mock from flexmock import flexmock from oto import response import pytest from product_digital.logic import product_workflow from product_digital.models import audio_product as audio_product_model from product_digital.models import ows_assets as assets_model from product_digital.models import ows_product_workflow as workflow_model from product_digital.models import ows_track as track_model from product_digital.models import release as release_model from product_digital.validation import audio_product as product_validator @pytest.fixture def correction_data(): """Return a sample payload for a release correction record.""" return {'status': 'active'} @pytest.fixture def rejections_data(): """A sample set of rejections data used for testing.""" return {'rejections': [{'rejection_id': 123, 'corrected': 'Y'}]} @pytest.fixture def rejections_update_message(): """A sample set of rejections data used for testing.""" return { 'rejections': [{ 'rejection_id': 123, 'field_name': 'general_comments', 'table_name': 'releases', 'comments': 'test', 'key_id': 0, 'corrected': 'Y'}]} @pytest.fixture def release_correction_id(): """Return a fake release correction id for testing.""" return 67890 @pytest.fixture def valid_release_correction(): """Return a fake release correction id for testing.""" return { 'release_correction_id': 6789, 'release_id': 12345, 'status': 'active', 'items': [{ 'release_correction_detail_id': 1, 'table_name': 'track', 'field_name': 'track', 'key_id': 1, 'key_value': True }, { 'release_correction_detail_id': 2, 'table_name': 'track', 'field_name': 'originalFileName', 'key_id': 1, 'key_value': 'originalFileName.flac' }, { 'release_correction_detail_id': 3, 'table_name': 'track', 'field_name': 'length_minute', 'key_id': 1, 'key_value': 1 }, { 'release_correction_detail_id': 4, 'table_name': 'track', 'field_name': 'length_seconds', 'key_id': 1, 'key_value': 23 }, { 'release_correction_detail_id': 5, 'table_name': 'track', 'field_name': 'last_updated', 'key_id': 1, 'key_value': '2019-07-10 17:04:26' }] } def test_update_rejections( monkeypatch, rejections_data, rejections_update_message): """Test that the logic calls the model layer and returns its response.""" model_mock = mock.Mock(return_value=rejections_update_message) monkeypatch.setattr(workflow_model, 'update_rejections', model_mock) result = product_workflow.update_rejections( rejections_data) assert result == rejections_update_message def test_create_release_correction_success( release_correction_id, correction_data, product_id): """Test that the logic calls the model layer and returns its response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True) (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) not_found_response = response.Response(status=404, message='nobody home') (flexmock(workflow_model).should_receive( 'get_last_release_correction').with_args( product_id).and_return(not_found_response)) correction_response = response.Response(status=201, message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_release_correction').once().with_args( product_id, correction_data).and_return( correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data) assert logic_response == correction_response def test_create_release_correction_vendor_user( correction_data, product_id): """Test that an alw orchard user overrides the last_updated fields.""" orchard_user_id = 'alw:123' expected_payload = copy.copy(correction_data) expected_payload.update({ 'last_updated_by': 123, 'last_updated_type': 'vendor' }) (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True) not_found_response = response.Response(status=404, message='nobody home') (flexmock(workflow_model).should_receive( 'get_last_release_correction').with_args( product_id).and_return(not_found_response)) correction_response = response.Response(status=201, message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_release_correction').once().with_args( product_id, expected_payload).and_return( correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data, orchard_user_id) assert logic_response.status == correction_response.status def test_create_release_correction_oa_user( correction_data, product_id): """Test that an oa orchard user overrides the last_updated fields.""" orchard_user_id = 'oa:123' expected_payload = copy.copy(correction_data) expected_payload.update({ 'last_updated_by': 123, 'last_updated_type': 'oa' }) (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) not_found_response = response.Response(status=404, message='nobody home') (flexmock(workflow_model).should_receive( 'get_last_release_correction').with_args( product_id).and_return(not_found_response)) correction_response = response.Response(message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_release_correction').once().with_args( product_id, expected_payload).and_return( correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data, orchard_user_id) assert logic_response.status == correction_response.status def test_delete_release_correction_success_with_artwork_submission_v2( product_id, release_correction_id): """Test the logic calls the model layer and returns its OK response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, {'items': {}}) assets_response = response.Response(message={'foo': 'bar'}) flexmock(assets_model) \ .should_receive('delete_product_corrections_v2') \ .with_args(product_id) \ .and_return(assets_response) correction_response = response.Response(message={'foo': 'baz'}) (flexmock(workflow_model).should_receive( 'delete_release_correction').once().with_args( release_correction_id).and_return( correction_response)) logic_response = product_workflow.delete_release_correction( product_id, release_correction_id) assert logic_response == correction_response def test_delete_release_correction_success_with_artwork_submission_v2_and_tracks( product_id, release_correction_id, valid_release_correction): """Test the logic calls the model layer and returns its OK response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, valid_release_correction) assets_response = response.Response(message={'foo': 'bar'}) flexmock(assets_model) \ .should_receive('delete_product_corrections_v2') \ .with_args(product_id) \ .and_return(assets_response) correction_response = response.Response(message={'foo': 'baz'}) (flexmock(workflow_model).should_receive( 'delete_release_correction').once().with_args( release_correction_id).and_return( correction_response)) logic_response = product_workflow.delete_release_correction( product_id, release_correction_id) assert logic_response == correction_response def test_create_correction_details_records_success( product_id, correction_details_data, release_correction_id): """Test that the logic calls the model layer and returns its response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, dict()) correction_response = response.Response(message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_correction_details_records').once().with_args( release_correction_id, correction_details_data) .and_return(correction_response)) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data, ) assert logic_response == correction_response def test_delete_release_correction_fails_validation(product_id, release_correction_id): """Test the logic calls the model layer and returns a validation error.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(False, dict()) logic_response = product_workflow.delete_release_correction( product_id, release_correction_id) assert logic_response.status == 400 assert logic_response.errors == { 'code': 'validation_error', 'message': 'The release_correction does not reference the correct ' 'product' } def test_create_correction_details_records_with_ownership( product_id, correction_details_data, release_correction_id, orchard_user_id): """Test that an alw orchard user overrides the last_updated fields.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, dict()) expected_payload = copy.copy(correction_details_data) expected_payload.get('items')[0].update({ 'last_updated_by': 123, 'last_updated_type': 'vendor' }) correction_response = response.Response(message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_correction_details_records').once().with_args( release_correction_id, expected_payload) .and_return(correction_response)) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data, orchard_user_id) assert logic_response.status == correction_response.status def test_create_release_correction_active_correction_exists( correction_data, product_id): """Test that error is returned if an active correction exists.""" (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) last_correction_response = response.Response(message={'status': 'active'}) (flexmock(workflow_model).should_receive( 'get_last_release_correction').once().with_args( product_id).and_return(last_correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data) assert logic_response.status == 409 assert logic_response.errors == { 'code': 'correction_conflict', 'message': 'Product has a correction in "active" status' } def test_create_release_correction_submitted_correction_exists( correction_data, product_id): """Test that error is returned if a submitted correction exists.""" (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) last_correction_response = response.Response( message={'status': 'submitted'}) (flexmock(workflow_model).should_receive( 'get_last_release_correction').once().with_args( product_id).and_return(last_correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data) assert logic_response.status == 409 assert logic_response.errors == { 'code': 'correction_conflict', 'message': 'Product has a correction in "submitted" status' } def test_create_release_correction_applied_correction_exists( correction_data, product_id): """Test that a new correction is created if applied correction exists.""" (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) last_correction_response = response.Response( message={'status': 'applied'}) (flexmock(workflow_model).should_receive( 'get_last_release_correction').with_args( product_id).and_return(last_correction_response)) correction_response = response.Response(status=201, message={'foo': 'bar'}) (flexmock(workflow_model).should_receive( 'create_release_correction').once().with_args( product_id, correction_data).and_return( correction_response)) logic_response = product_workflow.create_release_correction( product_id, correction_data) assert logic_response == correction_response def test_create_release_correction_get_fails( correction_data, product_id): """Test error handling when fetching existing correction returns error.""" (flexmock(release_model).should_receive('get_release').with_args( product_id).once().and_return(response.Response())) last_correction_response = response.Response(status=500, errors='fail') (flexmock(workflow_model).should_receive( 'get_last_release_correction').with_args( product_id).and_return(last_correction_response)) (flexmock(workflow_model).should_receive( 'create_release_correction').never()) logic_response = product_workflow.create_release_correction( product_id, correction_data) assert logic_response == last_correction_response def test_create_correction_details_records_fails_validation( product_id, release_correction_id, correction_details_data, orchard_user_id): """Test the logic calls the model layer and returns a validation error.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(False, dict()) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data, orchard_user_id) assert logic_response.status == 400 assert logic_response.errors == { 'code': 'validation_error', 'message': 'The release_correction does not reference the correct ' 'product' } def test_create_correction_details_records_when_genre_changed_success( product_id, correction_details_data_with_genre_fields, release_correction_id, change_genre_request_data, orchard_user_id): """Test that the logic calls the model layer and returns its response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, dict()) change_genre_request_data.update({'correction_id': release_correction_id}) correction_response = response.Response(message={'success'}, status=200) (flexmock(workflow_model).should_receive( 'create_correction_details_records').once().with_args( release_correction_id, correction_details_data_with_genre_fields) .and_return(correction_response)) (flexmock(track_model).should_receive( 'change_genre').once().with_args(product_id, change_genre_request_data, orchard_user_id) .and_return(correction_response)) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data_with_genre_fields, orchard_user_id) assert logic_response.status == correction_response.status def test_create_correction_details_for_subgenre_soundtrack_success( product_id, correction_details_data_with_subgenre_fields, release_correction_id, change_genre_request_data, orchard_user_id, correction_details_data_with_genre_fields, product_basics): """Test that the logic calls the model layer and returns its response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, dict()) change_genre_request_data.update({'correction_id': release_correction_id}) change_genre_request_data.update({'subgenre_id': 545}) correction_response = response.Response(message={'success'}, status=200) get_release_correction_response = response.Response( message=correction_details_data_with_genre_fields, status=200) (flexmock(workflow_model).should_receive( 'get_release_correction').once().with_args( release_correction_id) .and_return(get_release_correction_response)) (flexmock(workflow_model).should_receive( 'create_correction_details_records').once().with_args( release_correction_id, correction_details_data_with_subgenre_fields) .and_return(correction_response)) (flexmock(audio_product_model).should_receive('get_product').with_args( product_id).once().and_return(response.Response(message=product_basics, status=200))) (flexmock(track_model).should_receive( 'change_genre').once().with_args(product_id, change_genre_request_data, orchard_user_id) .and_return(correction_response)) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data_with_subgenre_fields, orchard_user_id) assert logic_response.status == correction_response.status def test_create_correction_for_subgenre_soundtrack_get_genre_id_from_releases_success( product_id, correction_details_data_with_subgenre_fields, release_correction_id, change_genre_request_data, orchard_user_id, product_basics): """Test that the logic calls the model layer and returns its response.""" flexmock(product_validator) \ .should_receive('validate_release_correction') \ .with_args(product_id, release_correction_id) \ .and_return(True, dict()) change_genre_request_data.update({'correction_id': release_correction_id}) change_genre_request_data.update({'subgenre_id': 545}) change_genre_request_data.update({'genre_id': 123}) correction_response = response.Response(message={'success'}, status=200) get_release_correction_response = response.Response( message=correction_details_data_with_subgenre_fields, status=200) (flexmock(workflow_model).should_receive( 'get_release_correction').once().with_args( release_correction_id) .and_return(get_release_correction_response)) (flexmock(workflow_model).should_receive( 'create_correction_details_records').once().with_args( release_correction_id, correction_details_data_with_subgenre_fields) .and_return(correction_response)) (flexmock(audio_product_model).should_receive('get_product').with_args( product_id).once().and_return(response.Response(message=product_basics, status=200))) (flexmock(track_model).should_receive( 'change_genre').once().with_args(product_id, change_genre_request_data, orchard_user_id) .and_return(correction_response)) logic_response = product_workflow.create_correction_details_records( product_id, release_correction_id, correction_details_data_with_subgenre_fields, orchard_user_id) assert logic_response.status == correction_response.status