python-boto3/tests/unit/resources/test_response.py
2022-05-25 16:13:54 -07:00

369 lines
12 KiB
Python

# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License'). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the 'license' file accompanying this file. This file is
# distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import pytest
from boto3.resources.base import ResourceMeta, ServiceResource
from boto3.resources.factory import ResourceFactory
from boto3.resources.model import Parameter, ResponseResource
from boto3.resources.response import (
RawHandler,
ResourceHandler,
build_empty_response,
build_identifiers,
)
from boto3.utils import ServiceContext
from tests import BaseTestCase, mock
class TestBuildIdentifiers(BaseTestCase):
def test_build_identifier_from_res_path_scalar(self):
identifiers = [
Parameter(target='Id', source='response', path='Container.Frob.Id')
]
parent = mock.Mock()
params = {}
response = {'Container': {'Frob': {'Id': 'response-path'}}}
values = build_identifiers(identifiers, parent, params, response)
# Verify identifier loaded from responsePath scalar set
assert values[0][1] == 'response-path'
def test_build_identifier_from_res_path_list(self):
identifiers = [
Parameter(
target='Id', source='response', path='Container.Frobs[].Id'
)
]
parent = mock.Mock()
params = {}
response = {'Container': {'Frobs': [{'Id': 'response-path'}]}}
values = build_identifiers(identifiers, parent, params, response)
# Verify identifier loaded from responsePath scalar set
assert values[0][1] == ['response-path']
def test_build_identifier_from_parent_identifier(self):
identifiers = [Parameter(target='Id', source='identifier', name='Id')]
parent = mock.Mock()
parent.id = 'identifier'
params = {}
response = {'Container': {'Frobs': []}}
values = build_identifiers(identifiers, parent, params, response)
# Verify identifier loaded from responsePath scalar set
assert values[0][1] == 'identifier'
def test_build_identifier_from_parent_data_member(self):
identifiers = [Parameter(target='Id', source='data', path='Member')]
parent = mock.Mock()
parent.meta = ResourceMeta('test', data={'Member': 'data-member'})
params = {}
response = {'Container': {'Frobs': []}}
values = build_identifiers(identifiers, parent, params, response)
# Verify identifier loaded from responsePath scalar set
assert values[0][1] == 'data-member'
def test_build_identifier_from_req_param(self):
identifiers = [
Parameter(target='Id', source='requestParameter', path='Param')
]
parent = mock.Mock()
params = {'Param': 'request-param'}
response = {'Container': {'Frobs': []}}
values = build_identifiers(identifiers, parent, params, response)
# Verify identifier loaded from responsePath scalar set
assert values[0][1] == 'request-param'
def test_build_identifier_from_invalid_source_type(self):
identifiers = [Parameter(target='Id', source='invalid')]
parent = mock.Mock()
params = {}
response = {'Container': {'Frobs': []}}
with pytest.raises(NotImplementedError):
build_identifiers(identifiers, parent, params, response)
class TestBuildEmptyResponse(BaseTestCase):
def setUp(self):
super().setUp()
self.search_path = ''
self.operation_name = 'GetFrobs'
self.output_shape = mock.Mock()
operation_model = mock.Mock()
operation_model.output_shape = self.output_shape
self.service_model = mock.Mock()
self.service_model.operation_model.return_value = operation_model
def get_response(self):
return build_empty_response(
self.search_path, self.operation_name, self.service_model
)
def test_empty_structure(self):
self.output_shape.type_name = 'structure'
response = self.get_response()
# Structure should default to empty dictionary
assert isinstance(response, dict)
assert response == {}
def test_empty_list(self):
self.output_shape.type_name = 'list'
response = self.get_response()
assert isinstance(response, list)
assert len(response) == 0
def test_empty_map(self):
self.output_shape.type_name = 'map'
response = self.get_response()
assert isinstance(response, dict)
assert response == {}
def test_empty_string(self):
self.output_shape.type_name = "string"
response = self.get_response()
assert response is None
def test_empty_integer(self):
self.output_shape.type_name = "integer"
response = self.get_response()
assert response is None
def test_empty_unknown_returns_none(self):
self.output_shape.type_name = "invalid"
response = self.get_response()
assert response is None
def test_path_structure(self):
self.search_path = 'Container.Frob'
frob = mock.Mock()
frob.type_name = 'integer'
container = mock.Mock()
container.type_name = 'structure'
container.members = {'Frob': frob}
self.output_shape.type_name = 'structure'
self.output_shape.members = {'Container': container}
response = self.get_response()
assert response is None
def test_path_list(self):
self.search_path = 'Container[1].Frob'
frob = mock.Mock()
frob.type_name = 'integer'
container = mock.Mock()
container.type_name = 'list'
container.member = frob
self.output_shape.type_name = 'structure'
self.output_shape.members = {'Container': container}
response = self.get_response()
assert response is None
def test_path_invalid(self):
self.search_path = 'Container.Invalid'
container = mock.Mock()
container.type_name = 'invalid'
self.output_shape.type_name = 'structure'
self.output_shape.members = {'Container': container}
with pytest.raises(NotImplementedError):
self.get_response()
class TestRawHandler(BaseTestCase):
def test_raw_handler_response(self):
parent = mock.Mock()
params = {}
response = {'Id': 'foo'}
handler = RawHandler(search_path=None)
parsed_response = handler(parent, params, response)
# verify response is unmodified
assert parsed_response == response
def test_raw_handler_response_path(self):
parent = mock.Mock()
params = {}
frob = {'Id': 'foo'}
response = {'Container': {'Frob': frob}}
handler = RawHandler(search_path='Container.Frob')
parsed_response = handler(parent, params, response)
assert parsed_response == frob
class TestResourceHandler(BaseTestCase):
def setUp(self):
super().setUp()
self.identifier_path = ''
self.factory = ResourceFactory(mock.Mock())
self.resource_defs = {
'Frob': {'shape': 'Frob', 'identifiers': [{'name': 'Id'}]}
}
self.service_model = mock.Mock()
shape = mock.Mock()
shape.members = {}
self.service_model.shape_for.return_value = shape
frobs = mock.Mock()
frobs.type_name = 'list'
container = mock.Mock()
container.type_name = 'structure'
container.members = {'Frobs': frobs}
self.output_shape = mock.Mock()
self.output_shape.type_name = 'structure'
self.output_shape.members = {'Container': container}
operation_model = mock.Mock()
operation_model.output_shape = self.output_shape
self.service_model.operation_model.return_value = operation_model
self.parent = mock.Mock()
self.parent.meta = ResourceMeta('test', client=mock.Mock())
self.params = {}
def get_resource(self, search_path, response):
request_resource_def = {
'type': 'Frob',
'identifiers': [
{
'target': 'Id',
'source': 'response',
'path': self.identifier_path,
},
],
}
resource_model = ResponseResource(
request_resource_def, self.resource_defs
)
handler = ResourceHandler(
search_path=search_path,
factory=self.factory,
resource_model=resource_model,
service_context=ServiceContext(
service_name='myservice',
resource_json_definitions=self.resource_defs,
service_model=self.service_model,
service_waiter_model=None,
),
operation_name='GetFrobs',
)
return handler(self.parent, self.params, response)
def test_create_resource_scalar(self):
self.identifier_path = 'Container.Id'
search_path = 'Container'
response = {
'Container': {
'Id': 'a-frob',
'OtherValue': 'other',
}
}
resource = self.get_resource(search_path, response)
assert isinstance(resource, ServiceResource)
@mock.patch('boto3.resources.response.build_empty_response')
def test_missing_data_scalar_builds_empty_response(self, build_mock):
self.identifier_path = 'Container.Id'
search_path = 'Container'
response = {'something': 'irrelevant'}
resources = self.get_resource(search_path, response)
assert build_mock.called
assert resources == build_mock.return_value
def test_create_resource_list(self):
self.identifier_path = 'Container.Frobs[].Id'
search_path = 'Container.Frobs[]'
response = {
'Container': {
'Frobs': [
{
'Id': 'a-frob',
'OtherValue': 'other',
},
{
'Id': 'another-frob',
'OtherValue': 'foo',
},
]
}
}
resources = self.get_resource(search_path, response)
assert isinstance(resources, list)
assert len(resources) == 2
assert isinstance(resources[0], ServiceResource)
def test_create_resource_list_no_search_path(self):
self.identifier_path = '[].Id'
search_path = ''
response = [{'Id': 'a-frob', 'OtherValue': 'other'}]
resources = self.get_resource(search_path, response)
assert isinstance(resources, list)
assert len(resources) == 1
assert isinstance(resources[0], ServiceResource)
@mock.patch('boto3.resources.response.build_empty_response')
def test_missing_data_list_builds_empty_response(self, build_mock):
self.identifier_path = 'Container.Frobs[].Id'
search_path = 'Container.Frobs[]'
response = {'something': 'irrelevant'}
resources = self.get_resource(search_path, response)
assert build_mock.called, 'build_empty_response was never called'
assert resources == build_mock.return_value