Skip to content

Commit 28de01c

Browse files
committed
fix tests
1 parent 8d787a4 commit 28de01c

File tree

8 files changed

+59
-60
lines changed

8 files changed

+59
-60
lines changed

localstack-core/localstack/services/apigateway/next_gen/execute_api/context.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ class IntegrationRequest(TypedDict, total=False):
3939
query_string_parameters: dict[str, str | list[str]]
4040
"""Query string parameters of the request"""
4141
headers: Headers
42-
# TODO: should this be Headers type? would maybe help access in the integrations if we need
43-
# currently, we support headers with same name and different casing, it's up to the integration to support it
4442
"""Headers of the request"""
4543
body: bytes
4644
"""Body content of the request"""

localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/integration_request.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
22

3+
from werkzeug.datastructures import Headers
4+
35
from localstack.aws.api.apigateway import Integration, IntegrationType
46
from localstack.constants import APPLICATION_JSON
57
from localstack.http import Response
@@ -89,7 +91,7 @@ def __call__(
8991
http_method=integration_method,
9092
uri=rendered_integration_uri,
9193
query_string_parameters=request_data_mapping["querystring"],
92-
headers=request_data_mapping["header"],
94+
headers=Headers(request_data_mapping["header"]),
9395
body=body,
9496
)
9597

localstack-core/localstack/services/apigateway/next_gen/execute_api/parameters_mapping.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ def map_integration_request(
5353
for header in ("Content-Type", "Accept"):
5454
request_data_mapping["header"][header] = APPLICATION_JSON
5555

56+
# storing the case-sensitive headers once, the mapping is strict
57+
case_sensitive_headers = dict(invocation_request["headers"].items())
58+
5659
for integration_mapping, request_mapping in request_parameters.items():
5760
integration_param_location, param_name = integration_mapping.removeprefix(
5861
"integration.request."
@@ -61,7 +64,7 @@ def map_integration_request(
6164
if request_mapping.startswith("method.request."):
6265
method_req_expr = request_mapping.removeprefix("method.request.")
6366
value = self._retrieve_parameter_from_invocation_request(
64-
method_req_expr, invocation_request
67+
method_req_expr, invocation_request, case_sensitive_headers
6568
)
6669

6770
else:
@@ -191,7 +194,10 @@ def _retrieve_parameter_from_integration_response(
191194
)
192195

193196
def _retrieve_parameter_from_invocation_request(
194-
self, expr: str, invocation_request: InvocationRequest
197+
self,
198+
expr: str,
199+
invocation_request: InvocationRequest,
200+
case_sensitive_headers: dict[str, str],
195201
) -> str | list[str] | None:
196202
"""
197203
See https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html#mapping-response-parameters
@@ -243,11 +249,15 @@ def _retrieve_parameter_from_invocation_request(
243249
return multi_qs_params
244250

245251
elif param_type == "header":
246-
multi_headers = invocation_request["headers"].getlist(param_name)
247-
if multi_headers:
248-
return multi_headers[-1]
252+
# casing is strict: if you don't specify the exact casing the server is returning, it does not map it to
253+
# the request
254+
return case_sensitive_headers.get(param_name)
249255

250256
elif param_type == "multivalueheader":
257+
# validate casing first
258+
if param_name not in case_sensitive_headers:
259+
return
260+
251261
multi_headers = invocation_request["headers"].getlist(param_name)
252262
return ",".join(multi_headers)
253263

tests/unit/services/apigateway/test_handler_api_key_validation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _create_context(
5151
context = RestApiInvocationContext(Request())
5252

5353
# The api key validator only relies on the raw headers from the invocation requests
54-
context.invocation_request = InvocationRequest(raw_headers=Headers(headers))
54+
context.invocation_request = InvocationRequest(headers=Headers(headers))
5555

5656
# Frozen deployment populated by the router
5757
context.deployment = RestApiDeployment(

tests/unit/services/apigateway/test_handler_integration_request.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from http import HTTPMethod
22

33
import pytest
4+
from werkzeug.datastructures import Headers
45

56
from localstack.aws.api.apigateway import Integration, IntegrationType, Method
67
from localstack.http import Request, Response
@@ -98,10 +99,12 @@ def test_noop(self, integration_request_handler, default_context):
9899
integration_request_handler(default_context)
99100
assert default_context.integration_request == {
100101
"body": b"",
101-
"headers": {
102-
"Accept": "application/json",
103-
"Content-Type": "application/json",
104-
},
102+
"headers": Headers(
103+
{
104+
"Accept": "application/json",
105+
"Content-Type": "application/json",
106+
}
107+
),
105108
"http_method": "POST",
106109
"query_string_parameters": {},
107110
"uri": "https://example.com",
@@ -205,11 +208,13 @@ def test_request_parameters(self, integration_request_handler, default_context):
205208
# TODO this test will fail when we implement uri mapping
206209
assert default_context.integration_request["uri"] == "https://example.com/path"
207210
assert default_context.integration_request["query_string_parameters"] == {"qs": "qs2"}
208-
assert default_context.integration_request["headers"] == {
209-
"header": "header2",
210-
"Accept": "application/json",
211-
"Content-Type": "application/json",
212-
}
211+
assert default_context.integration_request["headers"] == Headers(
212+
{
213+
"header": "header2",
214+
"Accept": "application/json",
215+
"Content-Type": "application/json",
216+
}
217+
)
213218

214219
def test_request_override(self, integration_request_handler, default_context):
215220
default_context.resource_method["methodIntegration"]["requestParameters"] = {
@@ -226,12 +231,14 @@ def test_request_override(self, integration_request_handler, default_context):
226231
assert default_context.integration_request["query_string_parameters"] == {
227232
"qs": "queryOverride"
228233
}
229-
assert default_context.integration_request["headers"] == {
230-
"header": "headerOverride",
231-
"multivalue": ["1header", "2header"],
232-
"Accept": "application/json",
233-
"Content-Type": "application/json",
234-
}
234+
assert default_context.integration_request["headers"] == Headers(
235+
{
236+
"header": "headerOverride",
237+
"multivalue": ["1header", "2header"],
238+
"Accept": "application/json",
239+
"Content-Type": "application/json",
240+
}
241+
)
235242

236243
def test_request_override_casing(self, integration_request_handler, default_context):
237244
default_context.resource_method["methodIntegration"]["requestParameters"] = {
@@ -242,12 +249,14 @@ def test_request_override_casing(self, integration_request_handler, default_cont
242249
}
243250
integration_request_handler(default_context)
244251
# TODO: for now, it's up to the integration to properly merge headers (`requests` does it automatically)
245-
assert default_context.integration_request["headers"] == {
246-
"myHeader": "header2",
247-
"myheader": "headerOverride",
248-
"Accept": "application/json",
249-
"Content-Type": "application/json",
250-
}
252+
assert default_context.integration_request["headers"] == Headers(
253+
{
254+
"myHeader": "header2",
255+
"myheader": "headerOverride",
256+
"Accept": "application/json",
257+
"Content-Type": "application/json",
258+
}
259+
)
251260

252261
def test_multivalue_mapping(self, integration_request_handler, default_context):
253262
default_context.resource_method["methodIntegration"]["requestParameters"] = {

tests/unit/services/apigateway/test_handler_method_request.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22

33
import pytest
4+
from werkzeug.datastructures import Headers
45

56
from localstack.aws.api.apigateway import Method, Model, RequestValidator, RestApi
67
from localstack.http import Request, Response
@@ -85,7 +86,7 @@ def test_validator_request_parameters(self, method_request_handler, dummy_contex
8586

8687
# Invocation with no valid element
8788
dummy_context.invocation_request = InvocationRequest(
88-
headers={}, query_string_parameters={}, path=""
89+
headers=Headers(), query_string_parameters={}, path=""
8990
)
9091
with pytest.raises(BadRequestParametersError) as e:
9192
method_request_handler(dummy_context)

tests/unit/services/apigateway/test_handler_request.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,13 @@ def test_parse_request(self, dummy_deployment, parse_handler_chain, get_invocati
9696
assert context.region == TEST_AWS_REGION_NAME
9797

9898
assert context.invocation_request["http_method"] == "GET"
99-
assert context.invocation_request["raw_headers"] == Headers(
99+
assert context.invocation_request["headers"] == Headers(
100100
{
101101
"host": host_header,
102102
"test-header": "value1",
103103
"test-header-multi": ["value2", "value3"],
104-
"content-length": len(body),
105104
}
106105
)
107-
assert context.invocation_request["headers"] == {
108-
"host": host_header,
109-
"test-header": "value1",
110-
"test-header-multi": "value2",
111-
"content-length": "11",
112-
}
113-
assert context.invocation_request["multi_value_headers"] == {
114-
"host": [host_header],
115-
"test-header": ["value1"],
116-
"test-header-multi": ["value2", "value3"],
117-
"content-length": ["11"],
118-
}
119106
assert context.invocation_request["body"] == body
120107
assert (
121108
context.invocation_request["path"]

tests/unit/services/apigateway/test_parameters_mapping.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def default_invocation_request() -> InvocationRequest:
4545
context = RestApiInvocationContext(
4646
Request(
4747
method=HTTPMethod.POST,
48-
headers={"header_value": "test-header-value"},
48+
headers=Headers({"header_value": "test-header-value"}),
4949
path=f"{TEST_API_STAGE}/test/test-path-value",
5050
query_string="qs_value=test-qs-value",
5151
)
@@ -336,14 +336,8 @@ def test_multi_headers_mapping(self, default_invocation_request, default_context
336336

337337
headers = {"testMultiHeader": ["value1", "value2"], "testHeader": "value"}
338338

339-
default_invocation_request["raw_headers"] = Headers(headers)
339+
default_invocation_request["headers"] = Headers(headers)
340340
# this is how AWS maps to the variables passed to proxy integration, it only picks the first of the multi values
341-
default_invocation_request["headers"] = {"testMultiHeader": "value1", "testHeader": "value"}
342-
343-
default_invocation_request["multi_value_headers"] = {
344-
"testMultiHeader": ["value1", "value2"],
345-
"testHeader": ["value"],
346-
}
347341

348342
mapping = mapper.map_integration_request(
349343
request_parameters=request_parameters,
@@ -415,9 +409,7 @@ def test_default_request_mapping_missing_request_values(self, default_context_va
415409
}
416410

417411
request = InvocationRequest(
418-
raw_headers=Headers(),
419-
headers={},
420-
multi_value_headers={},
412+
headers=Headers(),
421413
query_string_parameters={},
422414
multi_value_query_string_parameters={},
423415
path_parameters={},
@@ -467,8 +459,8 @@ def test_default_values_headers_request_mapping(
467459
self, default_invocation_request, default_context_variables
468460
):
469461
mapper = ParametersMapper()
470-
default_invocation_request["raw_headers"].add("Content-Type", "application/xml")
471-
default_invocation_request["raw_headers"].add("Accept", "application/xml")
462+
default_invocation_request["headers"].add("Content-Type", "application/xml")
463+
default_invocation_request["headers"].add("Accept", "application/xml")
472464

473465
mapping = mapper.map_integration_request(
474466
request_parameters={},
@@ -494,8 +486,8 @@ def test_default_values_headers_request_mapping_override(
494486
"integration.request.header.Content-Type": "method.request.header.header_value",
495487
"integration.request.header.accept": "method.request.header.header_value",
496488
}
497-
default_invocation_request["raw_headers"].add("Content-Type", "application/json")
498-
default_invocation_request["raw_headers"].add("Accept", "application/json")
489+
default_invocation_request["headers"].add("Content-Type", "application/json")
490+
default_invocation_request["headers"].add("Accept", "application/json")
499491

500492
mapping = mapper.map_integration_request(
501493
request_parameters=request_parameters,

0 commit comments

Comments
 (0)