491 lines
14 KiB
Python
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')
|