From 68fbca3714c207e6d8b58b300f329bdc2c147f93 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Wed, 18 Feb 2026 20:25:08 +0000 Subject: [PATCH 1/2] Add VuMark endpoint to parametrized fixture for auth testing Adds a vumark_generate_instance fixture to the endpoint parametrization, allowing all auth tests (missing header, malformed header, bad keys, etc.) to also cover the VuMark instance generation endpoint. Updates the Endpoint class to support endpoints with binary responses by making successful_headers_result_code optional. Updates test_date_header.py to handle VuMark's binary success response. Co-Authored-By: Claude Haiku 4.5 --- tests/conftest.py | 1 + tests/mock_vws/fixtures/prepared_requests.py | 48 ++++++++++++++++++++ tests/mock_vws/test_date_header.py | 12 +++++ tests/mock_vws/utils/__init__.py | 2 +- 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 64ef1427f..76b970470 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -85,6 +85,7 @@ def target_id( "target_summary", "update_target", "query", + "vumark_generate_instance", ], ) def endpoint(request: pytest.FixtureRequest) -> Endpoint: diff --git a/tests/mock_vws/fixtures/prepared_requests.py b/tests/mock_vws/fixtures/prepared_requests.py index 6a1d93aa8..724ea8e27 100644 --- a/tests/mock_vws/fixtures/prepared_requests.py +++ b/tests/mock_vws/fixtures/prepared_requests.py @@ -5,6 +5,7 @@ import json from http import HTTPMethod, HTTPStatus from typing import Any +from uuid import uuid4 import pytest from urllib3.filepost import encode_multipart_formdata @@ -13,6 +14,7 @@ from mock_vws._constants import ResultCodes from mock_vws.database import VuforiaDatabase +from tests.mock_vws.fixtures.credentials import VuMarkVuforiaDatabase from tests.mock_vws.utils import Endpoint from tests.mock_vws.utils.retries import RETRY_ON_TOO_MANY_REQUESTS @@ -451,3 +453,49 @@ def query( access_key=access_key, secret_key=secret_key, ) + + +@pytest.fixture +def vumark_generate_instance( + vumark_vuforia_database: VuMarkVuforiaDatabase, +) -> Endpoint: + """Return details of the endpoint for generating a VuMark instance.""" + request_path = f"/targets/{vumark_vuforia_database.target_id}/instances" + content_type = "application/json" + method = HTTPMethod.POST + content = json.dumps(obj={"instance_id": uuid4().hex}).encode( + encoding="utf-8" + ) + date = rfc_1123_date() + + access_key = vumark_vuforia_database.server_access_key + secret_key = vumark_vuforia_database.server_secret_key + authorization_string = authorization_header( + access_key=access_key, + secret_key=secret_key, + method=method, + content=content, + content_type=content_type, + date=date, + request_path=request_path, + ) + + headers = { + "Accept": "image/png", + "Authorization": authorization_string, + "Content-Length": str(object=len(content)), + "Content-Type": content_type, + "Date": date, + } + + return Endpoint( + successful_headers_status_code=HTTPStatus.OK, + successful_headers_result_code=None, + base_url=VWS_HOST, + path_url=request_path, + method=method, + headers=headers, + data=content, + access_key=access_key, + secret_key=secret_key, + ) diff --git a/tests/mock_vws/test_date_header.py b/tests/mock_vws/test_date_header.py index eea178ac3..d5e5f0b45 100644 --- a/tests/mock_vws/test_date_header.py +++ b/tests/mock_vws/test_date_header.py @@ -381,6 +381,12 @@ def test_date_in_range_after(endpoint: Endpoint) -> None: assert_query_success(response=response) return + if endpoint.successful_headers_result_code is None: + assert ( + response.status_code == endpoint.successful_headers_status_code + ) + return + assert_vws_response( response=response, status_code=endpoint.successful_headers_status_code, @@ -445,6 +451,12 @@ def test_date_in_range_before(endpoint: Endpoint) -> None: assert_query_success(response=response) return + if endpoint.successful_headers_result_code is None: + assert ( + response.status_code == endpoint.successful_headers_status_code + ) + return + assert_vws_response( response=response, status_code=endpoint.successful_headers_status_code, diff --git a/tests/mock_vws/utils/__init__.py b/tests/mock_vws/utils/__init__.py index c554e4571..d4bfa0014 100644 --- a/tests/mock_vws/utils/__init__.py +++ b/tests/mock_vws/utils/__init__.py @@ -47,7 +47,7 @@ class Endpoint: method: str headers: Mapping[str, str] data: bytes | str - successful_headers_result_code: ResultCodes + successful_headers_result_code: ResultCodes | None successful_headers_status_code: int access_key: str secret_key: str From 04c35f669e96874d3502c580b4aab7087de05154 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Wed, 18 Feb 2026 23:09:23 +0000 Subject: [PATCH 2/2] Fix invalid JSON test failure for VuMark endpoint Real Vuforia returns result_code 'BadRequest' (not 'Fail') when invalid JSON is sent to the VuMark instance generation endpoint. Adds the BAD_REQUEST result code, a BadRequestError exception, and updates validate_json() to raise the correct error based on the request path. Updates the test to expect BadRequest for the /instances endpoint. Co-Authored-By: Claude Sonnet 4.6 --- src/mock_vws/_constants.py | 1 + src/mock_vws/_services_validators/__init__.py | 2 +- .../_services_validators/exceptions.py | 40 +++++++++++++++++++ .../_services_validators/json_validators.py | 11 ++++- tests/mock_vws/test_invalid_json.py | 7 +++- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/mock_vws/_constants.py b/src/mock_vws/_constants.py index 68bf9375f..52c88bb9b 100644 --- a/src/mock_vws/_constants.py +++ b/src/mock_vws/_constants.py @@ -62,6 +62,7 @@ class ResultCodes(Enum): TOO_MANY_REQUESTS = "TooManyRequests" INVALID_ACCEPT_HEADER = "InvalidAcceptHeader" INVALID_INSTANCE_ID = "InvalidInstanceId" + BAD_REQUEST = "BadRequest" @beartype diff --git a/src/mock_vws/_services_validators/__init__.py b/src/mock_vws/_services_validators/__init__.py index 44487b365..03a39bd1c 100644 --- a/src/mock_vws/_services_validators/__init__.py +++ b/src/mock_vws/_services_validators/__init__.py @@ -103,7 +103,7 @@ def run_services_validators( validate_date_format(request_headers=request_headers) validate_date_in_range(request_headers=request_headers) - validate_json(request_body=request_body) + validate_json(request_body=request_body, request_path=request_path) validate_keys( request_body=request_body, diff --git a/src/mock_vws/_services_validators/exceptions.py b/src/mock_vws/_services_validators/exceptions.py index f7cbe2439..a722f29ab 100644 --- a/src/mock_vws/_services_validators/exceptions.py +++ b/src/mock_vws/_services_validators/exceptions.py @@ -184,6 +184,46 @@ def __init__(self, *, status_code: HTTPStatus) -> None: } +@beartype +class BadRequestError(ValidatorError): + """Exception raised when Vuforia returns a response with a result code + 'BadRequest'. + """ + + def __init__(self) -> None: + """ + Attributes: + status_code: The status code to use in a response if this is + raised. + response_text: The response text to use in a response if this + is + raised. + """ + super().__init__() + self.status_code = HTTPStatus.BAD_REQUEST + body = { + "transaction_id": uuid.uuid4().hex, + "result_code": ResultCodes.BAD_REQUEST.value, + } + self.response_text = json_dump(body=body) + date = email.utils.formatdate( + timeval=None, + localtime=False, + usegmt=True, + ) + self.headers = { + "Connection": "keep-alive", + "Content-Type": "application/json", + "server": "envoy", + "Date": date, + "x-envoy-upstream-service-time": "5", + "Content-Length": str(object=len(self.response_text)), + "strict-transport-security": "max-age=31536000", + "x-aws-region": "us-east-2, us-west-2", + "x-content-type-options": "nosniff", + } + + @beartype class MetadataTooLargeError(ValidatorError): """Exception raised when Vuforia returns a response with a result code diff --git a/src/mock_vws/_services_validators/json_validators.py b/src/mock_vws/_services_validators/json_validators.py index 9d04eec29..4e0549cd0 100644 --- a/src/mock_vws/_services_validators/json_validators.py +++ b/src/mock_vws/_services_validators/json_validators.py @@ -8,6 +8,7 @@ from beartype import beartype from mock_vws._services_validators.exceptions import ( + BadRequestError, FailError, UnnecessaryRequestBodyError, ) @@ -43,14 +44,18 @@ def validate_body_given(*, request_body: bytes, request_method: str) -> None: @beartype -def validate_json(*, request_body: bytes) -> None: +def validate_json(*, request_body: bytes, request_path: 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. Raises: - FailError: The request body includes invalid JSON. + BadRequestError: The request body includes invalid JSON for the + VuMark instance generation endpoint. + FailError: The request body includes invalid JSON for other + endpoints. """ if not request_body: return @@ -59,4 +64,6 @@ def validate_json(*, request_body: bytes) -> None: json.loads(s=request_body.decode()) except JSONDecodeError as exc: _LOGGER.warning(msg="The request body is not valid JSON.") + if request_path.endswith("/instances"): + raise BadRequestError from exc raise FailError(status_code=HTTPStatus.BAD_REQUEST) from exc diff --git a/tests/mock_vws/test_invalid_json.py b/tests/mock_vws/test_invalid_json.py index 55d01ff6d..35b3253cd 100644 --- a/tests/mock_vws/test_invalid_json.py +++ b/tests/mock_vws/test_invalid_json.py @@ -75,10 +75,15 @@ def test_invalid_json(endpoint: Endpoint) -> None: assert_valid_date_header(response=response) if takes_json_data: + expected_result_code = ( + ResultCodes.BAD_REQUEST + if endpoint.path_url.endswith("/instances") + else ResultCodes.FAIL + ) assert_vws_failure( response=response, status_code=HTTPStatus.BAD_REQUEST, - result_code=ResultCodes.FAIL, + result_code=expected_result_code, ) return