Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ jobs:
- tests/mock_vws/test_invalid_json.py::TestInvalidJSON::test_not_json
- tests/mock_vws/test_invalid_json.py::TestInvalidJSON::test_not_an_object
- tests/mock_vws/test_invalid_json.py::TestInvalidJSON::test_not_utf_8
- tests/mock_vws/test_invalid_json.py::TestInvalidJSON::test_empty_body
- tests/mock_vws/test_invalid_json.py::TestInvalidJSON::test_invalid_json_with_skewed_time
- tests/mock_vws/test_target_list.py
- tests/mock_vws/test_reco_counts_report.py
Expand Down
2 changes: 2 additions & 0 deletions newsfragments/3550.change
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Return a response, rather than raising an uncaught ``JSONDecodeError``, when an empty body is given to a VWS endpoint which takes a JSON body.
As real Vuforia does, ``POST /targets`` and ``PUT /targets/<target_id>`` now return a 500 ``Fail`` response, the reco counts report endpoint returns a 400 ``Fail`` response, and the VuMark instance generation endpoint returns a 400 ``BadRequest`` response.
6 changes: 5 additions & 1 deletion src/mock_vws/_services_validators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,11 @@ def run_services_validators[DatabaseT: AnyDatabase](
validate_date_format(request_headers=request_headers)
validate_date_in_range(request_headers=request_headers)

validate_json(request_body=request_body, request_path=request_path)
validate_json(
request_body=request_body,
request_path=request_path,
request_method=request_method,
)

validate_keys(
request_body=request_body,
Expand Down
30 changes: 24 additions & 6 deletions src/mock_vws/_services_validators/json_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,39 @@ def validate_body_given(*, request_body: bytes, request_method: str) -> None:


@beartype
def validate_json(*, request_body: bytes, request_path: str) -> None:
def validate_json(
*,
request_body: bytes,
request_path: str,
request_method: str,
) -> None:
"""Validate that any given body is valid JSON.

Args:
request_body: The body of the request.
request_path: The path of the request.
request_method: The HTTP method of the request.

Raises:
BadRequestError: The request body is not valid UTF-8, or includes
invalid JSON, for the VuMark instance generation endpoint.
FailError: The request body is not valid UTF-8, or includes invalid
JSON, for other endpoints.
BadRequestError: The request body is empty, is not valid UTF-8, or
includes invalid JSON, for the VuMark instance generation
endpoint.
FailError: The request body is empty, is not valid UTF-8, or includes
invalid JSON, for other endpoints.
"""
if not request_body:
return
if request_method not in {HTTPMethod.POST, HTTPMethod.PUT}:
return

_LOGGER.warning(msg="The request body is empty.")
if request_path.endswith("/instances"):
raise BadRequestError
# Vuforia reports a server error for an empty body given to the
# target endpoints, but a bad request for one given to the reco
# counts report endpoint.
if request_path.endswith("/reports/recoCounts"):
raise FailError(status_code=HTTPStatus.BAD_REQUEST)
raise FailError(status_code=HTTPStatus.INTERNAL_SERVER_ERROR)

try:
# Vuforia gives the same response for a body which is not UTF-8, such
Expand Down
79 changes: 79 additions & 0 deletions tests/mock_vws/test_invalid_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,85 @@ def test_not_utf_8(endpoint: Endpoint) -> None:
content = '{"name": "caf茅"}'.encode(encoding="latin-1")
_assert_body_rejected(endpoint=endpoint, content=content)

@staticmethod
def test_empty_body(endpoint: Endpoint) -> None:
"""Giving an empty body to an endpoint which takes JSON returns an
error response.

Real Vuforia gives a server error for an empty body given to the
target endpoints, but a bad request for one given to the reco counts
report endpoint or to the VuMark instance generation endpoint.

Unlike the other malformed bodies which this class sends, an empty
body is answered promptly by real Vuforia, so this test runs against
it.
"""
takes_json_data = (
endpoint.auth_header_content_type == "application/json"
)
if not takes_json_data:
pytest.skip(reason="This endpoint does not take a JSON body.")

if endpoint.path_url.endswith("/instances"):
expected_status_code = HTTPStatus.BAD_REQUEST
expected_result_code = ResultCodes.BAD_REQUEST
elif endpoint.path_url.endswith("/reports/recoCounts"):
expected_status_code = HTTPStatus.BAD_REQUEST
expected_result_code = ResultCodes.FAIL
else:
expected_status_code = HTTPStatus.INTERNAL_SERVER_ERROR
expected_result_code = ResultCodes.FAIL

content = b""
gmt = ZoneInfo(key="GMT")
now = datetime.now(tz=gmt)
time_to_freeze = now
with freeze_time(time_to_freeze=time_to_freeze):
date = rfc_1123_date()

authorization_string = authorization_header(
access_key=endpoint.access_key,
secret_key=endpoint.secret_key,
method=endpoint.method,
content=content,
content_type=endpoint.auth_header_content_type,
date=date,
request_path=endpoint.path_url,
)

new_headers = {
**endpoint.headers,
"Authorization": authorization_string,
"Date": date,
"Content-Length": str(object=len(content)),
}

new_endpoint = Endpoint(
base_url=endpoint.base_url,
path_url=endpoint.path_url,
method=endpoint.method,
headers=new_headers,
data=content,
successful_headers_result_code=endpoint.successful_headers_result_code,
successful_headers_status_code=endpoint.successful_headers_status_code,
access_key=endpoint.access_key,
secret_key=endpoint.secret_key,
)

response = new_endpoint.send()

# A server error is the expected response for some endpoints, so
# only treat one as transient where it is not expected.
if expected_status_code != HTTPStatus.INTERNAL_SERVER_ERROR:
handle_server_errors(response=response)

assert_valid_date_header(response=response)
assert_vws_failure(
response=response,
status_code=expected_status_code,
result_code=expected_result_code,
)

@staticmethod
def test_invalid_json_with_skewed_time(endpoint: Endpoint) -> None:
"""Giving invalid JSON to endpoints returns error responses."""
Expand Down
Loading