"""Lambda test module.""" import collections import datetime import json from unittest.mock import call from unittest.mock import patch import boto3 import moto import pytest import src.index as index import src.sql_queries as sql_queries from constants import fields import config UPCS = { 223344: {'upc': 111111111111, 'project_id': 112233}, 334455: {'upc': 2222222222222, 'project_id': 123456} } S3_VO_PRODUCTS = [ { 'project_id': 112233, 'product_id': 223344, 'upc': 111111111111, }, { 'project_id': 123456, 'product_id': 334455, 'upc': 2222222222222, }, ] @pytest.fixture def ar_order_details(): """Return get order query sample data.""" return [ { fields.AR_EO_ORDER_ID: 123, fields.AR_EO_ENCODER_ID: 18, fields.AR_EO_META_UPDATE: 'N', fields.AR_EO_PRIORITY: 1, fields.AR_EO_ENTRY_DATE: datetime.datetime( 2017, 8, 28, 15, 11, 52), fields.AR_EO_USER_ID: 1, fields.AR_EO_STORE_ID: 1, fields.AR_EO_PROJECT_ID: product.get('project_id'), fields.AR_EO_PRODUCT_ID: release_id, fields.AR_EO_UPC: product.get('upc'), } for release_id, product in UPCS.items() ] @pytest.fixture def encoding_order_dict(): """Return encoding order data.""" orders = collections.OrderedDict([ (123, { fields.AR_EO_ORDER_ID: 123, fields.AR_EO_ENCODER_ID: 18, fields.AR_EO_META_UPDATE: 'N', fields.AR_EO_PRIORITY: 1, fields.AR_EO_ENTRY_DATE: datetime.datetime( 2017, 8, 28, 15, 11, 52), fields.AR_EO_USER_ID: 1, fields.EO_DETAILS: [ { fields.AR_EO_STORE_ID: 1, fields.AR_EO_UPC: product.get('upc'), } for release_id, product in UPCS.items() ], fields.S3_VO_PRODUCTS: S3_VO_PRODUCTS, })] ) products = [ { 'project_id': 112233, 'product_id': 223344, 'upc': 111111111111, 'physical_location_id': 1, }, { 'project_id': 112233, 'product_id': 223344, 'upc': 111111111111, 'physical_location_id': 2, }, { 'project_id': 123456, 'product_id': 334455, 'upc': 2222222222222, 'physical_location_id': 1, }, { 'project_id': 123456, 'product_id': 334455, 'upc': 2222222222222, 'physical_location_id': 2, }, ] return {'orders': orders, 'products': products} @pytest.fixture def dynamo_dict(): """Return encoding(vector) order dynamo dict.""" return { fields.VO_ENCODER_ID: 18, fields.VO_ORDER_ID: '123', fields.VO_CREATED_AT: '2017-08-28T15:11:52', fields.DDB_VO_BUCKET_NAME: 'dev-vector-order', fields.VO_PRIORITY: 1, fields.DDB_VO_S3_KEY: 123, fields.VO_META_UPDATE: 'N', fields.VO_USER_ID: 1 } @pytest.fixture def s3_dict(): """Return encoding(vector) order s3 dict fixture.""" return { fields.VO_ENCODER_ID: 18, fields.VO_ORDER_ID: '123', fields.VO_CREATED_AT: '2017-08-28T15:11:52', fields.S3_VO_VALIDATED: { 2222222222222: [1], 111111111111: [1] }, fields.S3_VO_PRODUCTS: [111111111111, 2222222222222], fields.VO_PRIORITY: 1, fields.S3_VO_STORES: [1], fields.VO_META_UPDATE: 'N', fields.VO_USER_ID: 1, } def test_order_to_dynamo_dict(encoding_order_dict): """Test index.order_to_full_dict function.""" order = next(iter(encoding_order_dict.get('orders').values())) ddb_dict = index.order_to_dynamo_dict(order) assert fields.VO_ORDER_ID in ddb_dict assert fields.VO_CREATED_AT in ddb_dict assert fields.VO_PRIORITY in ddb_dict assert fields.VO_ENCODER_ID in ddb_dict assert fields.VO_META_UPDATE in ddb_dict assert fields.DDB_VO_BUCKET_NAME in ddb_dict assert fields.DDB_VO_S3_KEY in ddb_dict assert fields.VO_USER_ID in ddb_dict assert isinstance(ddb_dict[fields.VO_ORDER_ID], str) assert isinstance(ddb_dict[fields.DDB_VO_S3_KEY], str) @pytest.mark.parametrize( 'test_input, test_result', [('1 , 2 ', [1, 2]), (' 6 , 7', [6, 7])]) def test_get_encoder_ids(mocker, test_input, test_result): """Test index.get_encoder_ids function.""" mocked_config = mocker.patch('src.index.config') mocked_config.ORDER_ENCODER_ID_LIST = test_input result = index.get_encoder_ids() assert result == test_result @pytest.mark.parametrize('test_input', ['1.2', 'aaa', '8;7']) def test_get_encoder_ids_error(mocker, test_input): """Test index.get_encoder_ids, error case.""" mocked_config = mocker.patch('src.index.config') mocked_config.ORDER_ENCODER_ID_LIST = test_input with pytest.raises(ValueError): index.get_encoder_ids() def test_order_to_full_dict(encoding_order_dict): """Test index.order_to_full_dict function.""" details = next(iter(encoding_order_dict.get('orders').values())) s3_dict = index.order_to_full_dict(details) assert fields.VO_ORDER_ID in s3_dict assert fields.VO_CREATED_AT in s3_dict assert fields.VO_PRIORITY in s3_dict assert fields.VO_ENCODER_ID in s3_dict assert fields.VO_META_UPDATE in s3_dict assert fields.S3_VO_PRODUCTS in s3_dict assert fields.S3_VO_STORES in s3_dict assert fields.S3_VO_VALIDATED in s3_dict assert fields.VO_USER_ID in s3_dict assert isinstance(s3_dict[fields.VO_ORDER_ID], str) def test_get_s3_key(mocker): """Test index.get_s3_key function.""" mocked_s3_config = mocker.patch('src.index.config') mocked_s3_config.ORDERS_S3_PREFIX = 'prefix' result = index.get_s3_key('2000') assert result == 'prefix/2000.json' @moto.mock_aws def test_put_to_s3(s3_dict): """Test index.put_to_s3 function.""" s3_bucket_name = 'bucket' s3_key = 'key' # Get S3 connection mocked by moto. conn = boto3.resource('s3') # Pre-create the bucket. conn.create_bucket(Bucket=s3_bucket_name) index.put_to_s3(s3_bucket_name, s3_key, json.dumps(s3_dict)) # Read data from the mocked S3 object. s3_obj = conn.Object(s3_bucket_name, s3_key).get() body = s3_obj['Body'].read().decode() assert body == json.dumps(s3_dict) assert s3_obj['ContentType'] == 'application/json' @moto.mock_aws def test_put_to_dynamo_data(dynamo_dict): """Test index.put_to_dynamo function creates expected data in the DB.""" eo_id = dynamo_dict[fields.VO_ORDER_ID] # get dynamodb mock dynamodb = boto3.resource('dynamodb') # create table mock table = dynamodb.create_table( TableName=config.ORDERS_DDB_TABLE, KeySchema=[ { 'AttributeName': fields.VO_ORDER_ID, 'KeyType': 'HASH' } ], AttributeDefinitions=[ { 'AttributeName': fields.VO_ORDER_ID, 'AttributeType': 'S' } ], ProvisionedThroughput={ 'ReadCapacityUnits': 1, 'WriteCapacityUnits': 1 } ) index.put_to_dynamo(dynamo_dict) # read data from mock item = table.get_item(Key={fields.VO_ORDER_ID: str(eo_id)})['Item'] dynamo_eo_dict = dict(item) assert dynamo_eo_dict == dynamo_dict def test_get_dynamodb(mocker): """Test DynamoDB resource helper uses proper configuration.""" max_attempts = 42 mocked_config = mocker.patch('src.index.config') mocked_config.DDB_WRITE_MAX_ATTEMPTS = max_attempts mocked_botocore_config_instance = mocker.Mock() mocked_botocore_config = mocker.patch('src.index.botocore_config.Config') mocked_botocore_config.return_value = mocked_botocore_config_instance mocked_dynamodb = mocker.Mock() mocked_resource = mocker.patch('boto3.resource') mocked_resource.return_value = mocked_dynamodb assert index.get_dynamodb() == mocked_dynamodb mocked_botocore_config.assert_called_with( retries={'max_attempts': max_attempts}) mocked_resource.assert_called_with( 'dynamodb', config=mocked_botocore_config_instance) @patch('src.index.util.ar_connection') def test_get_orders( ar_connection, mocker, ar_order_details, encoding_order_dict): """Test index.get_orders function.""" encoder_ids = '1,2' # Does not matter if this matches the fixtures. limit = 1 conn_block = ar_connection.return_value.__enter__.return_value execute = conn_block.cursor.return_value.__enter__.return_value.execute fetch_all = conn_block.cursor.return_value.__enter__.return_value.fetchall fetch_all.return_value = ar_order_details mocked_config = mocker.patch('src.index.config') mocked_config.ORDERS_AR_SELECT_LIMIT = limit mocked_config.ORDER_ENCODER_ID_LIST = encoder_ids mocked_config.PHYSICAL_LOCATIONS = config.PHYSICAL_LOCATIONS orders, products = index.get_orders().values() assert list(orders.items()) == list(encoding_order_dict.get('orders').items()) assert products == encoding_order_dict.get('products') assert execute.call_args_list == [ call(sql_queries.GET_ENCODING_ORDERS.format( encoder_ids=encoder_ids, limit=1, )), call(sql_queries.GET_ENCODING_ORDER_DETAILS.format( order_ids='123,123', )), ] @patch('src.index.util.ar_connection') def test_update_order_status(ar_connection): """Test index.update_order_status function.""" # patch session scope status = 'Y' order_id = 1 index.update_order_status(order_id, status) conn_block = ar_connection.return_value.__enter__.return_value commit = conn_block.commit execute = conn_block.cursor.return_value.__enter__.return_value.execute expected_query = sql_queries.UPDATE_ENCODING_ORDER_STATUS.format( status=status, order_id=order_id) assert commit.call_count == 1 assert execute.call_count == 1 assert execute.call_args[0][0] == expected_query def test_handler(mocker, encoding_order_dict, s3_dict, dynamo_dict): """Test main handler method.""" mocked_get_orders = mocker.patch('src.index.get_orders') mocked_get_orders.return_value = encoding_order_dict mocked_order_to_dynamo_dict = mocker.patch('src.index.order_to_dynamo_dict') mocked_order_to_dynamo_dict.return_value = dynamo_dict mocked_order_to_full_dict = mocker.patch('src.index.order_to_full_dict') mocked_order_to_full_dict.return_value = s3_dict mocked_put_to_s3 = mocker.patch('src.index.put_to_s3') mocked_put_to_dynamo = mocker.patch('src.index.put_to_dynamo') mocked_update_order_status = mocker.patch('src.index.update_order_status') class LambdaContext: aws_request_id = '1234-1234' index.handler(None, LambdaContext) assert mocked_get_orders.call_args_list == [call()] assert mocked_order_to_full_dict.call_args_list == [ call( { 'encoding_order_id': 123, 'encoder_id': 18, 'meta_update': 'N', 'priority': 1, 'entry_date': datetime.datetime(2017, 8, 28, 15, 11, 52), 'orchadmin_user_id': 1, 'details': [ {'customer_master_master_id': 1, 'upc': 111111111111}, {'customer_master_master_id': 1, 'upc': 2222222222222} ], 'products': S3_VO_PRODUCTS } ) ] assert mocked_order_to_dynamo_dict.call_args_list == [ call( { 'encoding_order_id': 123, 'encoder_id': 18, 'meta_update': 'N', 'priority': 1, 'entry_date': datetime.datetime(2017, 8, 28, 15, 11, 52), 'orchadmin_user_id': 1, 'details': [ {'customer_master_master_id': 1, 'upc': 111111111111}, {'customer_master_master_id': 1, 'upc': 2222222222222} ], 'products': S3_VO_PRODUCTS } ) ] assert mocked_put_to_s3.call_args_list == [ call( 'dev-vector-order', 123, ( '{"encoder_id": 18, "order_id": "123", ' '"created_at": "2017-08-28T15:11:52", ' '"validated": {"2222222222222": [1], "111111111111": [1]}, ' '"products": [111111111111, 2222222222222], "priority": 1, ' '"stores": [1], "meta_update": "N", "user_id": 1}' ), ) ] assert mocked_put_to_dynamo.call_args_list == [ call( { 'encoder_id': 18, 'order_id': '123', 'created_at': '2017-08-28T15:11:52', 's3_bucket_name': 'dev-vector-order', 'priority': 1, 's3_key': 123, 'meta_update': 'N', 'user_id': 1 } ) ] assert mocked_update_order_status.call_args_list == [call(123, 'Y')]