|
1 | 1 | import base64 |
| 2 | +import datetime |
2 | 3 | import json |
3 | 4 | from dataclasses import dataclass |
4 | 5 | from enum import Enum |
|
20 | 21 | ) |
21 | 22 | from aws_lambda_powertools.event_handler.openapi.exceptions import ResponseValidationError |
22 | 23 | from aws_lambda_powertools.event_handler.openapi.params import Body, Form, Header, Query |
| 24 | +from tests.functional.utils import load_event |
23 | 25 |
|
24 | 26 |
|
25 | 27 | def test_validate_scalars(gw_event): |
@@ -1070,6 +1072,26 @@ def handler3(): |
1070 | 1072 | assert any(text in result["body"] for text in expected_error_text) |
1071 | 1073 |
|
1072 | 1074 |
|
| 1075 | +def test_validation_query_string_with_encoded_datetime_alb_resolver(): |
| 1076 | + # GIVEN a ALBResolver with validation enabled, |
| 1077 | + # and an event with a url-encoded datetime |
| 1078 | + # as a query string parameter |
| 1079 | + app = ALBResolver(enable_validation=True, decode_query_parameters=True) |
| 1080 | + raw_event = load_event("albEvent.json") |
| 1081 | + raw_event["path"] = "/users" |
| 1082 | + raw_event["queryStringParameters"] = {"query_dt": "2025-12-20T16%3A56%3A02.032000"} |
| 1083 | + |
| 1084 | + # WHEN a handler is defined with various parameters and routes |
| 1085 | + @app.get("/users") |
| 1086 | + def handler(query_dt: datetime.datetime): |
| 1087 | + return None |
| 1088 | + |
| 1089 | + # THEN the handler should be invoked with the expected result |
| 1090 | + # AND the status code should match the expected_status_code |
| 1091 | + result = app(raw_event, {}) |
| 1092 | + assert result["statusCode"] == 200 |
| 1093 | + |
| 1094 | + |
1073 | 1095 | @pytest.mark.parametrize( |
1074 | 1096 | "handler_func, expected_status_code, expected_error_text", |
1075 | 1097 | [ |
@@ -2672,3 +2694,60 @@ def query_model_advanced(params: Annotated[QueryAdvanced, Query()]) -> Dict[str, |
2672 | 2694 | full_name_error = next((e for e in errors if "full_name" in e["loc"] or "fullName" in e["loc"]), None) |
2673 | 2695 | assert full_name_error is not None |
2674 | 2696 | assert full_name_error["type"] == "string_too_short" |
| 2697 | + |
| 2698 | + |
| 2699 | +def test_validation_query_string_with_fully_encoded_datetime_alb_resolver(): |
| 2700 | + # GIVEN a ALBResolver with validation enabled, |
| 2701 | + # and an event with a fully url-encoded datetime |
| 2702 | + # as a query string parameter |
| 2703 | + app = ALBResolver(enable_validation=True, decode_query_parameters=True) |
| 2704 | + raw_event = load_event("albEvent.json") |
| 2705 | + raw_event["path"] = "/users" |
| 2706 | + # Fully encoded: "2025-12-20T16:56:02.032000" -> "2025-12-20T16%3A56%3A02.032000" |
| 2707 | + # With spaces or special chars: "2025-12-20 16:56:02" -> "2025-12-20%2016%3A56%3A02" |
| 2708 | + raw_event["queryStringParameters"] = {"query_dt": "2025-12-20T16%3A56%3A02.032000"} |
| 2709 | + |
| 2710 | + @app.get("/users") |
| 2711 | + def handler(query_dt: datetime.datetime): |
| 2712 | + return {"received": query_dt.isoformat()} |
| 2713 | + |
| 2714 | + result = app(raw_event, {}) |
| 2715 | + assert result["statusCode"] == 200 |
| 2716 | + body = json.loads(result["body"]) |
| 2717 | + assert body["received"] == "2025-12-20T16:56:02.032000" |
| 2718 | + |
| 2719 | + |
| 2720 | +def test_validation_query_string_with_encoded_key_and_value_alb_resolver(): |
| 2721 | + # GIVEN a ALBResolver with validation enabled, |
| 2722 | + # and an event with url-encoded key AND value |
| 2723 | + app = ALBResolver(enable_validation=True, decode_query_parameters=True) |
| 2724 | + raw_event = load_event("albEvent.json") |
| 2725 | + raw_event["path"] = "/search" |
| 2726 | + # Key: "search query" -> "search%20query" |
| 2727 | + # Value: "hello world" -> "hello%20world" |
| 2728 | + raw_event["queryStringParameters"] = {"search%20query": "hello%20world"} |
| 2729 | + |
| 2730 | + @app.get("/search") |
| 2731 | + def handler(search_query: Annotated[str, Query(alias="search query")]): |
| 2732 | + return {"result": search_query} |
| 2733 | + |
| 2734 | + result = app(raw_event, {}) |
| 2735 | + assert result["statusCode"] == 200 |
| 2736 | + body = json.loads(result["body"]) |
| 2737 | + assert body["result"] == "hello world" |
| 2738 | + |
| 2739 | + |
| 2740 | +def test_validation_without_decode_query_parameters_alb_resolver(): |
| 2741 | + # GIVEN a ALBResolver WITHOUT decode_query_parameters (default behavior) |
| 2742 | + app = ALBResolver(enable_validation=True) |
| 2743 | + raw_event = load_event("albEvent.json") |
| 2744 | + raw_event["path"] = "/users" |
| 2745 | + raw_event["queryStringParameters"] = {"query_dt": "2025-12-20T16%3A56%3A02.032000"} |
| 2746 | + |
| 2747 | + @app.get("/users") |
| 2748 | + def handler(query_dt: datetime.datetime): |
| 2749 | + return None |
| 2750 | + |
| 2751 | + # THEN validation should fail because the encoded string is not a valid datetime |
| 2752 | + result = app(raw_event, {}) |
| 2753 | + assert result["statusCode"] == 422 |
0 commit comments