Found by probing real Vuforia while verifying the fix for #3368. The status codes now match; the bodies do not.
What real Vuforia returns
Two distinct 404 responses, split by whether the path reaches the application:
Empty body, no Content-Type, Content-Length: 0, server: envoy, and no x-envoy-upstream-service-time header — for a path which does not start with a served path:
GET /some-random-endpoint -> 404, 0 bytes
GET /nonexistent -> 404, 0 bytes
GET /nonexistent/deeper -> 404, 0 bytes
GET /summar -> 404, 0 bytes
GET /duplicate -> 404, 0 bytes
GET /reports -> 404, 0 bytes
An HTML page, Content-Type: text/html; charset=UTF-8, gzipped, with an x-envoy-upstream-service-time header — for everything else it does not serve:
GET /summaryfoo -> 404, 1143 bytes
GET /summary-other -> 404, 1146 bytes
GET /targetsfoo -> 404, 1143 bytes
GET /duplicates -> 404, 1143 bytes
GET /summary/ -> 404, 1141 bytes
GET /targets/abc/nonexistent -> 404, 1156 bytes
POST /summary -> 404, 1141 bytes
PUT /summary -> 404, 1140 bytes
DELETE /summary -> 404, 1143 bytes
PATCH /targets -> 404, 1469 bytes
OPTIONS /summary -> 404, 1144 bytes
The page is the Play Framework "Not Found" page, and its only variable part names the request:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not Found</title>
<style type="text/css">
...
</style>
</head>
<body>
<h1>Not Found</h1>
<p id="detail">
For request 'DELETE /summary'
</p>
</body>
</html>
So the split is by path prefix: a path starting with a served path (/summary, /targets, /duplicates) is routed to the application, which renders the page; anything else is rejected at the edge with no body at all.
What the mock does
handle_unrouted_request in src/mock_vws/_flask_server/vws.py returns a 404 with an empty body and no Content-Type for every request it does not serve. That is exact for the edge case and wrong for the application case, where a caller sees no body, no Content-Type and no Content-Encoding.
Anyone asserting on the body of a 404, or branching on Content-Type, cannot tell the two cases apart through the mock.
Also noticed
HEAD /summary returns 400 with Content-Type: application/json and an empty body on real Vuforia, where the mock serves it from the GET /summary route and returns 200. That is a separate behaviour, recorded here so it is not lost.
Suggested resolution
Reproduce both shapes: return the Play page, with the request line filled in, for a path which starts with a served path, and keep the empty body otherwise. The prefix set is derivable from the routes the app already declares.
Whatever is decided, update the "Paths which the mock does not serve" section of docs/source/differences-to-vws.rst, which currently records this as a difference, and extend tests/mock_vws/test_unrouted_requests.py, which asserts the body only for the empty-body case.
Found by probing real Vuforia while verifying the fix for #3368. The status codes now match; the bodies do not.
What real Vuforia returns
Two distinct 404 responses, split by whether the path reaches the application:
Empty body, no
Content-Type,Content-Length: 0,server: envoy, and nox-envoy-upstream-service-timeheader — for a path which does not start with a served path:An HTML page,
Content-Type: text/html; charset=UTF-8, gzipped, with anx-envoy-upstream-service-timeheader — for everything else it does not serve:The page is the Play Framework "Not Found" page, and its only variable part names the request:
So the split is by path prefix: a path starting with a served path (
/summary,/targets,/duplicates) is routed to the application, which renders the page; anything else is rejected at the edge with no body at all.What the mock does
handle_unrouted_requestinsrc/mock_vws/_flask_server/vws.pyreturns a 404 with an empty body and noContent-Typefor every request it does not serve. That is exact for the edge case and wrong for the application case, where a caller sees no body, noContent-Typeand noContent-Encoding.Anyone asserting on the body of a 404, or branching on
Content-Type, cannot tell the two cases apart through the mock.Also noticed
HEAD /summaryreturns 400 withContent-Type: application/jsonand an empty body on real Vuforia, where the mock serves it from theGET /summaryroute and returns 200. That is a separate behaviour, recorded here so it is not lost.Suggested resolution
Reproduce both shapes: return the Play page, with the request line filled in, for a path which starts with a served path, and keep the empty body otherwise. The prefix set is derivable from the routes the app already declares.
Whatever is decided, update the "Paths which the mock does not serve" section of
docs/source/differences-to-vws.rst, which currently records this as a difference, and extendtests/mock_vws/test_unrouted_requests.py, which asserts the body only for the empty-body case.