python-botocore/tests/functional/test_context_params.py
2022-12-12 08:14:19 -08:00

491 lines
14 KiB
Python

# Copyright 2022 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
#
# http://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 botocore.config import Config
from tests import ClientHTTPStubber, mock
# fake rulesets compatible with all fake service models below
FAKE_RULESET_TEMPLATE = {
"version": "1.0",
"parameters": {},
"rules": [
{
"conditions": [],
"type": "endpoint",
"endpoint": {
"url": "https://foo.bar",
"properties": {},
"headers": {},
},
}
],
}
# The region param is unrelated to context parameters and used as control in
# all test cases to ascertain that ANY EndpointProvider paramaters get
# populated.
REGION_PARAM = {
"builtIn": "AWS::Region",
"required": False,
"documentation": "",
"type": "String",
}
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS = {
**FAKE_RULESET_TEMPLATE,
"parameters": {
"Region": REGION_PARAM,
},
}
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM = {
**FAKE_RULESET_TEMPLATE,
"parameters": {
"Region": REGION_PARAM,
"FooClientContextParamName": {
"required": False,
"documentation": "",
"type": "String",
},
},
}
FAKE_RULESET_WITH_STATIC_CONTEXT_PARAM = {
**FAKE_RULESET_TEMPLATE,
"parameters": {
"Region": REGION_PARAM,
"FooStaticContextParamName": {
"required": False,
"documentation": "",
"type": "String",
},
},
}
FAKE_RULESET_WITH_DYNAMIC_CONTEXT_PARAM = {
**FAKE_RULESET_TEMPLATE,
"parameters": {
"Region": REGION_PARAM,
"FooDynamicContextParamName": {
"required": False,
"documentation": "",
"type": "String",
},
},
}
# fake models for "otherservice"
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS = {
"version": "2.0",
"documentation": "",
"metadata": {
"apiVersion": "2020-02-02",
"endpointPrefix": "otherservice",
"protocol": "rest-xml",
"serviceFullName": "Other Service",
"serviceId": "Other Service",
"signatureVersion": "v4",
"signingName": "otherservice",
"uid": "otherservice-2020-02-02",
},
"operations": {
"MockOperation": {
"name": "MockOperation",
"http": {"method": "GET", "requestUri": "/"},
"input": {"shape": "MockOperationRequest"},
"documentation": "",
},
},
"shapes": {
"MockOpParam": {
"type": "string",
},
"MockOperationRequest": {
"type": "structure",
"required": ["MockOpParam"],
"members": {
"MockOpParam": {
"shape": "MockOpParam",
"documentation": "",
"location": "uri",
"locationName": "param",
},
},
},
},
}
FAKE_MODEL_WITH_CLIENT_CONTEXT_PARAM = {
**FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
"clientContextParams": {
"FooClientContextParamName": {
"documentation": "My mock client context parameter",
"type": "string",
}
},
}
FAKE_MODEL_WITH_STATIC_CONTEXT_PARAM = {
**FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
"operations": {
"MockOperation": {
"name": "MockOperation",
"http": {"method": "GET", "requestUri": "/"},
"input": {"shape": "MockOperationRequest"},
"documentation": "",
"staticContextParams": {
"FooStaticContextParamName": {
"value": "foo-static-context-param-value"
}
},
},
},
}
FAKE_MODEL_WITH_DYNAMIC_CONTEXT_PARAM = {
**FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
"shapes": {
"MockOpParam": {
"type": "string",
},
"MockOperationRequest": {
"type": "structure",
"required": ["MockOpParam"],
"members": {
"MockOpParam": {
"shape": "MockOpParam",
"documentation": "",
"location": "uri",
"locationName": "param",
"contextParam": {"name": "FooDynamicContextParamName"},
},
},
},
},
#
}
# fake models for s3 and s3control, the only services botocore currently
# supports client context parameters for
S3_METADATA = {
"apiVersion": "2006-03-01",
"checksumFormat": "md5",
"endpointPrefix": "s3",
"globalEndpoint": "s3.amazonaws.com",
"protocol": "rest-xml",
"serviceAbbreviation": "Amazon S3",
"serviceFullName": "Amazon Simple Storage Service",
"serviceId": "S3",
"signatureVersion": "s3",
"uid": "s3-2006-03-01",
}
FAKE_S3_MODEL_WITHOUT_ANY_CONTEXT_PARAMS = {
**FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
"metadata": S3_METADATA,
}
FAKE_S3_MODEL_WITH_CLIENT_CONTEXT_PARAM = {
**FAKE_MODEL_WITH_CLIENT_CONTEXT_PARAM,
"metadata": S3_METADATA,
}
S3CONTROL_METADATA = {
"apiVersion": "2018-08-20",
"endpointPrefix": "s3-control",
"protocol": "rest-xml",
"serviceFullName": "AWS S3 Control",
"serviceId": "S3 Control",
"signatureVersion": "s3v4",
"signingName": "s3",
"uid": "s3control-2018-08-20",
}
FAKE_S3CONTROL_MODEL_WITHOUT_ANY_CONTEXT_PARAMS = {
**FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
"metadata": S3CONTROL_METADATA,
}
FAKE_S3CONTROL_MODEL_WITH_CLIENT_CONTEXT_PARAM = {
**FAKE_MODEL_WITH_CLIENT_CONTEXT_PARAM,
"metadata": S3CONTROL_METADATA,
}
def patch_load_service_model(
session, monkeypatch, service_model_json, ruleset_json
):
def mock_load_service_model(service_name, type_name, api_version=None):
if type_name == 'service-2':
return service_model_json
if type_name == 'endpoint-rule-set-1':
return ruleset_json
loader = session.get_component('data_loader')
monkeypatch.setattr(loader, 'load_service_model', mock_load_service_model)
@pytest.mark.parametrize(
'service_name,service_model,ruleset,call_should_include_ctx_param',
[
# s3
(
's3',
FAKE_S3_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
True,
),
(
's3',
FAKE_S3_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
(
's3',
FAKE_S3_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
False,
),
(
's3',
FAKE_S3_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
# s3control
(
's3control',
FAKE_S3CONTROL_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
True,
),
(
's3control',
FAKE_S3CONTROL_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
(
's3control',
FAKE_S3CONTROL_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
False,
),
(
's3control',
FAKE_S3CONTROL_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
# botocore does not currently support client context params for
# services other than s3 and s3-control.
(
'otherservice',
FAKE_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
False, # would be True for s3 and s3control
),
(
'otherservice',
FAKE_MODEL_WITH_CLIENT_CONTEXT_PARAM,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False, # same as for s3 and s3control
),
(
'otherservice',
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITH_CLIENT_CONTEXT_PARAM,
False, # same as for s3 and s3control
),
(
'otherservice',
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False, # same as for s3 and s3control
),
],
)
def test_client_context_param_sent_to_endpoint_resolver(
monkeypatch,
patched_session,
service_name,
service_model,
ruleset,
call_should_include_ctx_param,
):
# patch loader to return fake service model and fake endpoint ruleset
patch_load_service_model(
patched_session, monkeypatch, service_model, ruleset
)
# construct client using patched loader and a config object with an s3
# section that sets the foo_context_param to a value
client = patched_session.create_client(
service_name,
region_name='us-east-1',
config=Config(
s3={'foo_client_context_param_name': 'foo_context_param_value'}
),
)
# Stub client to prevent a request from getting sent and asceertain that
# only a single request would get sent. Wrap the EndpointProvider's
# resolve_endpoint method for inspecting the arguments it gets called with.
with ClientHTTPStubber(client, strict=True) as http_stubber:
http_stubber.add_response(status=200)
with mock.patch.object(
client._ruleset_resolver._provider,
'resolve_endpoint',
wraps=client._ruleset_resolver._provider.resolve_endpoint,
) as mock_resolve_endpoint:
client.mock_operation(MockOpParam='mock-op-param-value')
if call_should_include_ctx_param:
mock_resolve_endpoint.assert_called_once_with(
Region='us-east-1',
FooClientContextParamName='foo_context_param_value',
)
else:
mock_resolve_endpoint.assert_called_once_with(Region='us-east-1')
@pytest.mark.parametrize(
'service_name,service_model,ruleset,call_should_include_ctx_param',
[
(
'otherservice',
FAKE_MODEL_WITH_STATIC_CONTEXT_PARAM,
FAKE_RULESET_WITH_STATIC_CONTEXT_PARAM,
True,
),
(
'otherservice',
FAKE_MODEL_WITH_STATIC_CONTEXT_PARAM,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
(
'otherservice',
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITH_STATIC_CONTEXT_PARAM,
False,
),
(
'otherservice',
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
],
)
def test_static_context_param_sent_to_endpoint_resolver(
monkeypatch,
patched_session,
service_name,
service_model,
ruleset,
call_should_include_ctx_param,
):
# patch loader to return fake service model and fake endpoint ruleset
patch_load_service_model(
patched_session, monkeypatch, service_model, ruleset
)
# construct client using patched loader, but no special config is required
# for static context param to take effect
client = patched_session.create_client(
service_name, region_name='us-east-1'
)
with ClientHTTPStubber(client, strict=True) as http_stubber:
http_stubber.add_response(status=200)
with mock.patch.object(
client._ruleset_resolver._provider,
'resolve_endpoint',
wraps=client._ruleset_resolver._provider.resolve_endpoint,
) as mock_resolve_endpoint:
client.mock_operation(MockOpParam='mock-op-param-value')
if call_should_include_ctx_param:
mock_resolve_endpoint.assert_called_once_with(
Region='us-east-1',
FooStaticContextParamName='foo-static-context-param-value',
)
else:
mock_resolve_endpoint.assert_called_once_with(Region='us-east-1')
@pytest.mark.parametrize(
'service_name,service_model,ruleset,call_should_include_ctx_param',
[
(
'otherservice',
FAKE_MODEL_WITH_DYNAMIC_CONTEXT_PARAM,
FAKE_RULESET_WITH_DYNAMIC_CONTEXT_PARAM,
True,
),
(
'otherservice',
FAKE_MODEL_WITH_DYNAMIC_CONTEXT_PARAM,
FAKE_RULESET_WITHOUT_ANY_CONTEXT_PARAMS,
False,
),
(
'otherservice',
FAKE_MODEL_WITHOUT_ANY_CONTEXT_PARAMS,
FAKE_RULESET_WITH_DYNAMIC_CONTEXT_PARAM,
False,
),
],
)
def test_dynamic_context_param_sent_to_endpoint_resolver(
monkeypatch,
patched_session,
service_name,
service_model,
ruleset,
call_should_include_ctx_param,
):
# patch loader to return fake service model and fake endpoint ruleset
patch_load_service_model(
patched_session, monkeypatch, service_model, ruleset
)
# construct client using patched loader, but no special config is required
# for static context param to take effect
client = patched_session.create_client(
service_name, region_name='us-east-1'
)
with ClientHTTPStubber(client, strict=True) as http_stubber:
http_stubber.add_response(status=200)
with mock.patch.object(
client._ruleset_resolver._provider,
'resolve_endpoint',
wraps=client._ruleset_resolver._provider.resolve_endpoint,
) as mock_resolve_endpoint:
client.mock_operation(MockOpParam='mock-op-param-value')
if call_should_include_ctx_param:
mock_resolve_endpoint.assert_called_once_with(
Region='us-east-1',
FooDynamicContextParamName='mock-op-param-value',
)
else:
mock_resolve_endpoint.assert_called_once_with(Region='us-east-1')