"""Test graphQL connector.""" from unittest.mock import call from unittest.mock import MagicMock from unittest.mock import patch import pytest from owsclient import M2MTokenManager from content_utils.connectors.graphql import LambdaGraphQLConnector from content_utils.connectors.system_user import SystemUser from content_utils.constants.graphql import ADD_PRODUCT_MUTATION from content_utils.constants.graphql import COMPLETE_PRODUCT_QUERY from content_utils.constants.graphql import GENRE_QUERY from content_utils.constants.graphql import INDEXABLE_PRODUCT_QUERY from content_utils.constants.graphql import REVIEW_QUEUE_ITEM_QUERY from content_utils.constants.graphql import META_LANGUAGE_QUERY from content_utils.constants.product import PRODUCT_CONFIGURATION_DIGITAL_AUDIO from content_utils.exceptions import GatewayTimeoutException from content_utils.exceptions import GraphQLError from content_utils.exceptions import InvalidProductException from content_utils.exceptions import NoProductDataException from content_utils.exceptions import NoReviewQueueItemDataException from content_utils.exceptions import SoundRecordingsException from content_utils.utils.product_metadata import get_complete_product_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_request(mock_endpoint): """Test graphQL request.""" mock_endpoint.return_value = MagicMock(json=MagicMock(return_value={ 'data': { 'product': { 'productConfiguration': PRODUCT_CONFIGURATION_DIGITAL_AUDIO } } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.execute('query {hi}', 1) assert result.get('data').get('product').get('productConfiguration') == \ PRODUCT_CONFIGURATION_DIGITAL_AUDIO @patch('content_utils.connectors.graphql.OwsClient') def test_graphql_connector_initialization_with_TokenManager(mock_ows_client): """Test graphQL connector initialization with a token manager.""" test_logger = MagicMock() test_m2m_token_manager = MagicMock(spec=M2MTokenManager) test_conn = LambdaGraphQLConnector( graphql_service_name='graphql-service', application_name='foo', environment='dev', logger=test_logger, m2m_token_manager=test_m2m_token_manager, correlation_id='corr-id-1234' ) assert test_conn.graphql_service_name == 'graphql-service' assert test_conn.logger == test_logger assert test_conn.correlation_id == 'corr-id-1234' mock_ows_client.assert_called_once_with( service_name='foo', environment='dev', m2m_token_manager=test_m2m_token_manager, ) @patch('content_utils.connectors.graphql.OwsClient') def test_graphql_connector_initialization_without_TokenManager(mock_ows_client): """Test graphQL connector initialization without a token manager.""" test_logger = MagicMock() test_conn = LambdaGraphQLConnector( graphql_service_name='graphql-service', application_name='foo', environment='dev', logger=test_logger, correlation_id='corr-id-1234' ) assert test_conn.graphql_service_name == 'graphql-service' assert test_conn.logger == test_logger assert test_conn.correlation_id == 'corr-id-1234' mock_ows_client.assert_called_once_with( service_name='foo', environment='dev', m2m_token_manager=None, ) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_request_invalid_product_configuration(mock_endpoint): """Test graphQL request.""" mock_endpoint.return_value = MagicMock(json=MagicMock(return_value={ 'data': { 'product': { 'productConfiguration': 'Physical Audio' } } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) with pytest.raises(InvalidProductException): test_conn.get_indexable_product({'release_id': 1}) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_indexable_product( mock_gql, mock_common_graphql_query_args, mock_graphql_response, mock_graphql_review_queue_item_response, mock_row_data, mock_index_document ): """Test graphQL product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)) ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'POST_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ) ] assert result == mock_index_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_indexable_product_custom_profile( mock_gql, mock_common_graphql_query_args, mock_graphql_response, mock_graphql_review_queue_item_response, mock_row_data, mock_index_document ): """Test graphQL product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)) ] mock_system_user = SystemUser(profile_id=1, profile_type='FooProfile') mock_common_graphql_query_args.update({ 'profile_id': 1, 'profile_type': 'FooProfile' }) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), user=mock_system_user ) result = test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'PRE_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ) ] assert result == mock_index_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_indexable_product_no_review_queue_item( mock_gql, mock_common_graphql_query_args, mock_graphql_response, mock_graphql_review_queue_item_response, mock_row_data, mock_index_document ): """Test getting indexable product when no review queue item returned.""" mock_graphql_review_queue_item_response['data']['reviewQueueItem'] = None mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)) ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) with pytest.raises( NoReviewQueueItemDataException, match='Invalid review queue item: no data found for review queue item 1', ): test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'POST_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ) ] @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_d3_subaccount_indexable_product( mock_gql, mock_common_graphql_query_args, mock_graphql_d3_subaccount_response, mock_row_data, mock_d3_subaccount_index_document, mock_graphql_review_queue_item_response, ): """Test graphQL d3 subaccount product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_d3_subaccount_response)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)) ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'POST_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ) ] assert result == mock_d3_subaccount_index_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_indexable_product_with_profile( mock_gql, mock_common_graphql_query_args, mock_graphql_response, mock_row_data, mock_index_document, mock_graphql_review_queue_item_response, ): """Test graphQL product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)) ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'POST_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ) ] assert result == mock_index_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_indexable_product_with_corrections( mock_gql, mock_common_graphql_query_args, mock_graphql_response_with_corrections, mock_genre_response, mock_language_response, mock_row_data, mock_index_document, mock_formatted_corrections, mock_graphql_review_queue_item_response, ): """Test graphQL product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response_with_corrections)), MagicMock(json=MagicMock(return_value=mock_graphql_review_queue_item_response)), MagicMock(json=MagicMock(return_value=mock_genre_response)), MagicMock(json=MagicMock(return_value=mock_language_response)), ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_indexable_product(mock_row_data) assert mock_gql.mock_calls == [ call( query=INDEXABLE_PRODUCT_QUERY, variables={ 'productId': '1', 'validationContext': 'POST_SUBMISSION', 'showOnDemand': True }, **mock_common_graphql_query_args ), call( query=REVIEW_QUEUE_ITEM_QUERY, variables={'reviewQueueId': 1}, **mock_common_graphql_query_args ), call( query=GENRE_QUERY, variables={'genreId': 16}, **mock_common_graphql_query_args ), call( query=META_LANGUAGE_QUERY, variables={}, **mock_common_graphql_query_args ) ] mock_index_document.update(mock_formatted_corrections) mock_index_document['release_correction_status'] = 'submitted' mock_index_document['metadata_language_name'] = 'German' mock_index_document['genre_name'] = 'Classical' mock_graphql_review_queue_item_response['submission_type'] = 'revision' assert result == mock_index_document @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_add_product(mock_gql, mock_common_graphql_query_args): """Test graphQL request.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': { 'createReviewQueueItem': { 'reviewQueueId': 1 } } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.execute_add_to_queue_mutation( 1, 'af74c4b6-3b34-4eb1-9375-2222000ff00c', '1975-04-09 09:17:24') mock_gql.return_value.assert_has_calls == [ call( query=ADD_PRODUCT_MUTATION, variables={ 'productId': 1, 'identityId': 'af74c4b6-3b34-4eb1-9375-2222000ff00c', 'submissionDatetime': '1975-04-09 09:17:24' }, **mock_common_graphql_query_args ), ] assert result.get('createReviewQueueItem').get('reviewQueueId') == 1 @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_error(mock_gql, mock_graphql_base_product_response): """Test graphQL response errors.""" mock_gql.return_value = MagicMock(json=MagicMock( return_value=mock_graphql_base_product_response)) test_conn_raises = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(GraphQLError): test_conn_raises.get_base_product({}) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_error_null_product(mock_gql): """Test graphQL response errors.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={'data': {'product': None}})) test_conn_raises = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(NoProductDataException): test_conn_raises.get_base_product({}) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_get_base_product(mock_gql, mock_graphql_base_product_response): """Test graphQL base product query.""" mock_gql.return_value = MagicMock(json=MagicMock( return_value=mock_graphql_base_product_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_base_product({}) assert result == { 'assigned_reviewer_id': 101, 'assigned_to_id': 123, 'company_brand': { 'uuid': '0fd602fc-76b0-42fd-8607-2a0ddeca46e7', 'name': 'brandie', }, 'configuration': 'Digital Audio', 'display_status': 'submitted', 'distribution_format_id': 1, 'format': 'Full Length', 'label_id': 1, 'label_owner': 'odd', 'product_id': 1, 'product_name': 'This is the name.', 'release_correction_status': '', 'service_tier_name': 'applesauce-bananas', 'service_tier_uuid': '93185c0c-fe01-4bff-bb0d-a7609b8711e5', 'subaccount_id': 0, 'upc': '23456789234562' } @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_get_base_d3_subaccount_product( mock_gql, mock_graphql_base_d3_subaccount_product_response): """Test graphql for d3 subaccount base product data.""" mock_gql.return_value = MagicMock(json=MagicMock( return_value=mock_graphql_base_d3_subaccount_product_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_base_product({}) assert result == { 'assigned_reviewer_id': 1, 'assigned_to_id': 1, 'company_brand': { 'uuid': '0fd602fc-76b0-42fd-8607-2a0ddeca46e7', 'name': 'brandie', }, 'configuration': 'Digital Audio', 'display_status': 'action_required', 'distribution_format_id': 1, 'format': 'Full Length', 'label_id': 2, 'label_owner': 'odd', 'product_id': 2, 'product_name': 'This is the name.', 'release_correction_status': 'foo', 'service_tier_name': 'applesauce-bananas', 'service_tier_uuid': '93185c0c-fe01-4bff-bb0d-a7609b8711e5', 'subaccount_id': 3, 'upc': '12345678912343', } @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_add_product_error_raises(mock_gql): """Test graphQL request.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [ { 'extensions': { 'response': { 'body': 'err' } } } ] })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) with pytest.raises(GraphQLError): test_conn.execute_add_to_queue_mutation( 1, 'af74c4b6-3b34-4eb1-9375-2222000ff00c', '1975-04-09 09:17:24') @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_add_product_already_exists(mock_gql): """Test graphQL request.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [ { 'extensions': { 'response': { 'body': { 'code': 'bad_request', 'message': 'Product 1 already exists in queue' } } } } ] })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) with pytest.raises(InvalidProductException): test_conn.execute_add_to_queue_mutation( 1, 'af74c4b6-3b34-4eb1-9375-2222000ff00c', '1975-04-09 09:17:24') @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_sound_recordings_error(mock_gql, mock_row_data): """Test graphQL request track validation sound recordings error.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [{ 'message': 'Failed to query ows-sound-recordings for track validations.', 'path': ['validation'], }] })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(SoundRecordingsException): test_conn.get_indexable_product(mock_row_data) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_gateway_timeout_error(mock_gql, mock_row_data): """Test graphQL request 504 gateway timeout error.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [{ 'status': 504, 'message': '504: Gateway Timeout', 'path': ['validation'], }] })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(GatewayTimeoutException): test_conn.get_indexable_product(mock_row_data) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_product_configuration_error(mock_gql, mock_row_data): """Test graphQL unsupported product configuration error.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [{ 'message': 'Product configuration Physical Audio is unsupported.', 'path': ['validation'], }] })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(InvalidProductException): test_conn.get_indexable_product(mock_row_data) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_genre_by_id(mock_gql, mock_genre_response): """Test Grapqhl request get genre by id success.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value=mock_genre_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_genre_by_id(12) assert result == {'id': 12, 'name': 'Classical'} assert mock_gql.call_args.kwargs['query'] == GENRE_QUERY assert mock_gql.call_args.kwargs['variables'] == {'genreId': 12} @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_genre_by_id_not_found(mock_gql): """Test Grapqhl request get genre by id not found.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': { 'genres': [] } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_genre_by_id(12) assert not result assert mock_gql.call_args.kwargs['query'] == GENRE_QUERY assert mock_gql.call_args.kwargs['variables'] == {'genreId': 12} @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_language_for_code(mock_gql, mock_language_response): """Test Grapqhl request get language name by code success.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value=mock_language_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_language_for_code('ENG') assert result == 'English' assert mock_gql.call_args.kwargs['query'] == META_LANGUAGE_QUERY assert mock_gql.call_args.kwargs['variables'] == {} @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_language_for_code_not_found(mock_gql, mock_language_response): """Test Grapqhl request get language name by code not found.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value=mock_language_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_language_for_code('FRE') assert result == '' assert mock_gql.call_args.kwargs['query'] == META_LANGUAGE_QUERY assert mock_gql.call_args.kwargs['variables'] == {} @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_no_review_queue_item(mock_gql): """Test review queue item query error handling.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [{ 'message': 'Cannot return null for non-nullable field Query.reviewQueueItem.', 'path': ['reviewQueueItem'], 'extensions': {'code': 'INTERNAL_SERVER_ERROR'} }], 'extensions': { 'valueCompletion': [{ 'message': 'Cannot return null for non-nullable field Query.reviewQueueItem', 'path': [] }] } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(NoReviewQueueItemDataException): test_conn.fetch_review_queue_item_data(1) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_graphql_no_review_queue_item_and_other_errors(mock_gql): """Test review queue item query error handling.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value={ 'data': {}, 'errors': [ { 'message': 'Cannot return null for non-nullable field Query.reviewQueueItem.', 'path': ['reviewQueueItem'], 'extensions': {'code': 'INTERNAL_SERVER_ERROR'} }, { 'message': 'Other error', 'path': ['reviewQueueItem1'], 'extensions': {'code': 'INTERNAL_SERVER_ERROR'} } ], 'extensions': { 'valueCompletion': [ { 'message': 'Cannot return null for non-nullable field Query.reviewQueueItem', 'path': [] }, { 'message': 'Other error', 'path': [] } ] } })) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock(), raise_on_error=True ) with pytest.raises(GraphQLError): test_conn.fetch_review_queue_item_data(1) @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_locked_indexable_product( mock_gql, mock_graphql_response, mock_graphql_locked_review_queue_item_response, mock_row_data, mock_index_document ): """Test graphQL product request.""" mock_gql.side_effect = [ MagicMock(json=MagicMock(return_value=mock_graphql_response)), MagicMock(json=MagicMock(return_value=mock_graphql_locked_review_queue_item_response)) ] test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) result = test_conn.get_indexable_product(mock_row_data) expected = { **mock_index_document, 'locked_by_user_id': 'ab2dd624-367f-4a96-99a3-10b19f01598f', 'locked_until_datetime': '2023-10-12T15:35:07.659792Z' } assert result == expected @patch('content_utils.connectors.graphql.OwsClient.graphql_query') def test_get_complete_product( mock_gql, mock_common_graphql_query_args, mock_graphql_response ): """Test get_complete_product method.""" mock_gql.return_value = MagicMock(json=MagicMock(return_value=mock_graphql_response)) test_conn = LambdaGraphQLConnector( application_name='foo', environment='dev', logger=MagicMock() ) product_id = 1 result = test_conn.get_complete_product(product_id) mock_gql.assert_called_once_with( query=COMPLETE_PRODUCT_QUERY, variables={'productId': '1', 'validationContext': 'POST_SUBMISSION'}, **mock_common_graphql_query_args ) expected_document = get_complete_product_document( mock_graphql_response['data']['product'] ) assert result == expected_document