def test_query_parser_flattened_list_structure(): """Simple test with a flattened list of structures.""" parser = QueryRequestParser(load_service("sqs")) request = HttpRequest( body=to_bytes( "Action=DeleteMessageBatch&" "Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" "DeleteMessageBatchRequestEntry.1.Id=bar&" "DeleteMessageBatchRequestEntry.1.ReceiptHandle=foo&" "DeleteMessageBatchRequestEntry.2.Id=bar&" "DeleteMessageBatchRequestEntry.2.ReceiptHandle=foo"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "DeleteMessageBatch" assert params == { "QueueUrl": "http://localhost:4566/000000000000/tf-acc-test-queue", "Entries": [{ "Id": "bar", "ReceiptHandle": "foo" }, { "Id": "bar", "ReceiptHandle": "foo" }], }
def test_s3_virtual_host_addressing(): """Test the parsing of a map with the location trait 'headers'.""" request = HttpRequest( method="PUT", headers={"host": s3_utils.get_bucket_hostname("test-bucket")}) parser = create_parser(load_service("s3")) parsed_operation_model, parsed_request = parser.parse(request) assert parsed_operation_model.name == "CreateBucket" assert "Bucket" in parsed_request assert parsed_request["Bucket"] == "test-bucket"
def _botocore_parser_integration_test(service: str, action: str, headers: dict = None, expected: dict = None, **kwargs): # Load the appropriate service service = load_service(service) # Use the serializer from botocore to serialize the request params serializer = create_serializer(service.protocol) operation_model = service.operation_model(action) serialized_request = serializer.serialize_to_request( kwargs, operation_model) prepare_request_dict(serialized_request, "") split_url = urlsplit(serialized_request.get("url")) path = split_url.path query_string = split_url.query body = serialized_request["body"] # use custom headers (if provided), or headers from serialized request as default headers = serialized_request.get("headers") if headers is None else headers if service.protocol in ["query", "ec2"]: # Serialize the body as query parameter body = urlencode(serialized_request["body"]) # Use our parser to parse the serialized body parser = create_parser(service) parsed_operation_model, parsed_request = parser.parse( HttpRequest( method=serialized_request.get("method") or "GET", path=unquote(path), query_string=to_str(query_string), headers=headers, body=body, raw_path=path, )) # Check if the determined operation_model is correct assert parsed_operation_model == operation_model # Check if the result is equal to the given "expected" dict or the kwargs (if "expected" has not been set) expected = expected or kwargs # The parser adds None for none-existing members on purpose. Remove those for the assert expected = { key: value for key, value in expected.items() if value is not None } parsed_request = { key: value for key, value in parsed_request.items() if value is not None } assert parsed_request == expected
def test_restjson_operation_detection_with_subpath(): """ Tests if the operation lookup correctly fails for a subpath of an operation. For example: The detection of a URL which is routed through API Gateway. """ service = load_service("apigateway") parser = create_parser(service) with pytest.raises(OperationNotFoundParserError): parser.parse( HttpRequest( method="GET", path="/restapis/cmqinv79uh/local/_user_request_/", raw_path="/restapis/cmqinv79uh/local/_user_request_/", ))
def test_missing_required_field_restjson(self): parser = create_parser(load_service("opensearch")) op, params = parser.parse( HttpRequest( "POST", "/2021-01-01/tags", body='{"ARN":"somearn"}', )) with pytest.raises(MissingRequiredField) as e: validate_request(op, params).raise_first() assert e.value.error.reason == "missing required field" assert e.value.required_name == "TagList"
def test_parser_error_on_protocol_error(): """Test that the parser raises a ProtocolParserError in case of invalid data to parse.""" parser = QueryRequestParser(load_service("sqs")) request = HttpRequest( body=to_bytes( "Action=UnknownOperation&Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&" "DelaySeconds=2"), method="POST", headers={}, path="", ) with pytest.raises(ProtocolParserError): parser.parse(request)
def test_missing_required_field_restxml(self): parser = create_parser(load_service("route53")) op, params = parser.parse( HttpRequest( "POST", "/2013-04-01/hostedzone", body= "<CreateHostedZoneRequest><Name>foobar.com</Name></CreateHostedZoneRequest>", )) with pytest.raises(MissingRequiredField) as e: validate_request(op, params).raise_first() assert e.value.error.reason == "missing required field" assert e.value.required_name == "CallerReference"
def test_query_parser_non_flattened_list_structure(): """Simple test with a non-flattened list structure (CloudFormation CreateChangeSet).""" parser = QueryRequestParser(load_service("cloudformation")) request = HttpRequest( body=to_bytes( "Action=CreateChangeSet&" "ChangeSetName=SampleChangeSet&" "Parameters.member.1.ParameterKey=KeyName&" "Parameters.member.1.UsePreviousValue=true&" "Parameters.member.2.ParameterKey=Purpose&" "Parameters.member.2.ParameterValue=production&" "StackName=arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/1a2345b6-0000-00a0-a123-00abc0abc000&" "UsePreviousTemplate=true&" "Version=2010-05-15&" "X-Amz-Algorithm=AWS4-HMAC-SHA256&" "X-Amz-Credential=[Access-key-ID-and-scope]&" "X-Amz-Date=20160316T233349Z&" "X-Amz-SignedHeaders=content-type;host&" "X-Amz-Signature=[Signature]"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "CreateChangeSet" assert params == { "StackName": "arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/1a2345b6-0000-00a0-a123-00abc0abc000", "UsePreviousTemplate": True, "Parameters": [ { "ParameterKey": "KeyName", "UsePreviousValue": True }, { "ParameterKey": "Purpose", "ParameterValue": "production" }, ], "ChangeSetName": "SampleChangeSet", }
def test_invalid_length_query(self): parser = create_parser(load_service("sts")) op, params = parser.parse( HttpRequest( "POST", "/", body=urlencode( query={ "Action": "AssumeRole", "RoleArn": "arn:aws", # min=8 "RoleSessionName": "foobared", }), headers={"Content-Type": "application/x-www-form-urlencoded"}, )) with pytest.raises(InvalidLength) as e: validate_request(op, params).raise_first() e.match("RoleArn")
def test_query_parser(): """Basic test for the QueryParser with a simple example (SQS SendMessage request).""" parser = QueryRequestParser(load_service("sqs")) request = HttpRequest( body=to_bytes( "Action=SendMessage&Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&" "DelaySeconds=2"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "SendMessage" assert params == { "QueueUrl": "http://localhost:4566/000000000000/tf-acc-test-queue", "MessageBody": '{"foo": "bared"}', "DelaySeconds": 2, }
def test_invalid_range_query(self): parser = create_parser(load_service("sts")) op, params = parser.parse( HttpRequest( "POST", "/", body=urlencode( query={ "Action": "AssumeRole", "RoleArn": "arn:aws:iam::000000000000:role/foobared", "RoleSessionName": "foobared", "DurationSeconds": "100", }), headers={"Content-Type": "application/x-www-form-urlencoded"}, )) with pytest.raises(InvalidRange) as e: validate_request(op, params).raise_first() e.match("DurationSeconds")
def test_parser_error_on_unknown_error(): """Test that the parser raises a UnknownParserError in case of an unknown exception.""" parser = QueryRequestParser(load_service("sqs")) request = HttpRequest( body=to_bytes( "Action=SendMessage&Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" "MessageBody=%7B%22foo%22%3A+%22bared%22%7D&" "DelaySeconds=2"), method="POST", headers={}, path="", ) # An unknown error is obviously hard to trigger (because we would fix it if we would know of a way to trigger it), # therefore we patch a function to raise an unexpected error def raise_error(*args, **kwargs): raise NotImplementedError() parser._process_member = raise_error with pytest.raises(UnknownParserError): parser.parse(request)
def test_query_parser_non_flattened_list_structure_changed_name(): """Simple test with a non-flattened list structure where the name of the list differs from the shape's name (CloudWatch PutMetricData).""" parser = QueryRequestParser(load_service("cloudwatch")) request = HttpRequest( body=to_bytes( "Action=PutMetricData&" "Version=2010-08-01&" "Namespace=TestNamespace&" "MetricData.member.1.MetricName=buffers&" "MetricData.member.1.Unit=Bytes&" "MetricData.member.1.Value=231434333&" "MetricData.member.1.Dimensions.member.1.Name=InstanceType&" "MetricData.member.1.Dimensions.member.1.Value=m1.small&" "AUTHPARAMS"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "PutMetricData" assert params == { "MetricData": [{ "Dimensions": [{ "Name": "InstanceType", "Value": "m1.small" }], "MetricName": "buffers", "Unit": "Bytes", "Value": 231434333.0, }], "Namespace": "TestNamespace", }
def test_missing_required_field_query(self): parser = create_parser(load_service("sqs")) op, params = parser.parse( HttpRequest( "POST", "/", body= ("Action=SendMessage&Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" ), headers={"Content-Type": "application/x-www-form-urlencoded"}, )) validator = ParamValidator() errors = validator.validate(params, op.input_shape) assert errors.has_errors() with pytest.raises(MissingRequiredField) as e: errors.raise_first() assert e.match("MessageBody") assert e.value.error.reason == "missing required field" assert e.value.required_name == "MessageBody"
def test_query_parser_flattened_map(): """Simple test with a flattened map (SQS SetQueueAttributes request).""" parser = QueryRequestParser(load_service("sqs")) request = HttpRequest( body=to_bytes( "Action=SetQueueAttributes&Version=2012-11-05&" "QueueUrl=http%3A%2F%2Flocalhost%3A4566%2F000000000000%2Ftf-acc-test-queue&" "Attribute.1.Name=DelaySeconds&" "Attribute.1.Value=10&" "Attribute.2.Name=MaximumMessageSize&" "Attribute.2.Value=131072&" "Attribute.3.Name=MessageRetentionPeriod&" "Attribute.3.Value=259200&" "Attribute.4.Name=ReceiveMessageWaitTimeSeconds&" "Attribute.4.Value=20&" "Attribute.5.Name=RedrivePolicy&" "Attribute.5.Value=%7B%22deadLetterTargetArn%22%3A%22arn%3Aaws%3Asqs%3Aus-east-1%3A80398EXAMPLE%3AMyDeadLetterQueue%22%2C%22maxReceiveCount%22%3A%221000%22%7D&" "Attribute.6.Name=VisibilityTimeout&Attribute.6.Value=60"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "SetQueueAttributes" assert params == { "QueueUrl": "http://localhost:4566/000000000000/tf-acc-test-queue", "Attributes": { "DelaySeconds": "10", "MaximumMessageSize": "131072", "MessageRetentionPeriod": "259200", "ReceiveMessageWaitTimeSeconds": "20", "RedrivePolicy": '{"deadLetterTargetArn":"arn:aws:sqs:us-east-1:80398EXAMPLE:MyDeadLetterQueue","maxReceiveCount":"1000"}', "VisibilityTimeout": "60", }, }
def test_query_parser_non_flattened_map(): """Simple test with a flattened map (SQS SetQueueAttributes request).""" parser = QueryRequestParser(load_service("sns")) request = HttpRequest( body=to_bytes( "Action=SetEndpointAttributes&" "EndpointArn=arn%3Aaws%3Asns%3Aus-west-2%3A123456789012%3Aendpoint%2FGCM%2Fgcmpushapp%2F5e3e9847-3183-3f18-a7e8-671c3a57d4b3&" "Attributes.entry.1.key=CustomUserData&" "Attributes.entry.1.value=My+custom+userdata&" "Version=2010-03-31&" "AUTHPARAMS"), method="POST", headers={}, path="", ) operation, params = parser.parse(request) assert operation.name == "SetEndpointAttributes" assert params == { "Attributes": { "CustomUserData": "My custom userdata" }, "EndpointArn": "arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3", }