Skip to content
Open
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
15 changes: 15 additions & 0 deletions docs/source/differences-to-vws.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,21 @@ signature.
The 404 has not been verified, because no request for a real report has caught
one before it was generated.

Paths which the mock does not serve
-----------------------------------

Real Vuforia returns a 404 response for a request to a path which it does not
serve.

The Flask and Docker mock does the same, with a Flask error page as the body,
and it returns a 405 response for a request to a served path with a method
which that path does not serve.
Neither response body has been verified against real Vuforia.

The ``requests`` and ``httpx`` backends mock only the paths which the mock
serves, so a request to any other path raises a connection error rather than
returning a response.

Header cases
------------

Expand Down
1 change: 1 addition & 0 deletions newsfragments/unrouted-requests.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Return a 404 response from the Flask and Docker mock for a request to a path which it does not serve, and a 405 response for a request to a served path with a method which that path does not serve, rather than raising an error.
6 changes: 6 additions & 0 deletions src/mock_vws/_flask_server/vws.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,13 @@ def validate_request() -> None:

Reco counts report downloads stand in for presigned URLs, which are not
authorized with VWS credentials.

Flask runs ``before_request`` handlers before it raises a routing error,
so requests which match no route reach this function.
Those requests are left to Flask, which raises the routing error itself.
"""
if request.url_rule is None:
return
if request.endpoint == "generate_vumark_instance":
return
if (
Expand Down
82 changes: 82 additions & 0 deletions tests/mock_vws/test_flask_app_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,88 @@ def test_per_endpoint_limits() -> None:
client.get_database_summary_report()


class TestUnroutedRequests:
"""Tests for requests which the Flask app does not route.

These tests use the Flask test client because the ``responses``
library intercepts only the paths and methods which the app routes,
so requests to any other path never reach the app.
"""

@staticmethod
def _signed_headers(
*,
database: CloudDatabase,
method: HTTPMethod,
request_path: str,
) -> dict[str, str]:
"""Return headers which sign a request with valid server keys."""
date = rfc_1123_date()
authorization_string = authorization_header(
access_key=database.server_access_key,
secret_key=database.server_secret_key,
method=method,
content=b"",
content_type="",
date=date,
request_path=request_path,
)
return {"Authorization": authorization_string, "Date": date}

def test_unknown_path(self) -> None:
"""A request to a path which is not routed returns a 404."""
database = CloudDatabase()
databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + "/cloud_databases"
requests.post(url=databases_url, json=database.to_dict(), timeout=30)

request_path = "/some-random-endpoint"
headers = self._signed_headers(
database=database,
method=HTTPMethod.GET,
request_path=request_path,
)

response = VWS_FLASK_APP.test_client().get(
request_path,
headers=headers,
)

assert response.status_code == HTTPStatus.NOT_FOUND

def test_unknown_method(self) -> None:
"""A request to a routed path with a method which that path does
not serve returns a 405.
"""
database = CloudDatabase()
databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + "/cloud_databases"
requests.post(url=databases_url, json=database.to_dict(), timeout=30)

request_path = "/summary"
headers = self._signed_headers(
database=database,
method=HTTPMethod.POST,
request_path=request_path,
)

response = VWS_FLASK_APP.test_client().post(
request_path,
headers=headers,
)

assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED

@staticmethod
def test_unauthenticated_unknown_path() -> None:
"""A request to a path which is not routed returns a 404 even
without credentials.

The Docker health check relies on this request not erroring.
"""
response = VWS_FLASK_APP.test_client().get("/some-random-endpoint")

assert response.status_code == HTTPStatus.NOT_FOUND


class TestAddCloudDatabase:
"""Tests for adding cloud databases to the mock."""

Expand Down
Loading