You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.9 KiB

"""
Tests for automatic HEAD method support on GET routes (issue #1773).
FastAPI should automatically handle HEAD requests on any route that supports
GET, mirroring Starlette's behaviour (starlette/routing.py Route.__init__).
The HEAD method should be supported at the routing level but must NOT appear
as a separate operation in the OpenAPI schema.
"""
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/items/{item_id}")
def get_item(item_id: str):
return {"item_id": item_id}
@app.get("/ping")
def ping():
return {"status": "ok"}
@app.post("/submit")
def submit():
return {"result": "created"}
client = TestClient(app)
# ---------------------------------------------------------------------------
# Routing: HEAD should succeed for GET routes
# ---------------------------------------------------------------------------
def test_head_on_get_route_returns_200():
response = client.head("/items/foo")
assert response.status_code == 200, response.text
def test_head_on_get_route_returns_empty_body():
"""HEAD responses must have no body."""
response = client.head("/items/foo")
assert response.content == b""
def test_head_response_has_same_headers_as_get():
"""Content-Type set on GET should also be present on HEAD."""
get_response = client.get("/ping")
head_response = client.head("/ping")
assert head_response.status_code == 200
assert head_response.headers.get("content-type") == get_response.headers.get(
"content-type"
)
def test_post_route_still_works():
"""POST on the submit route must still work normally."""
response = client.post("/submit")
assert response.status_code == 200
assert response.json() == {"result": "created"}
def test_head_on_post_only_route_returns_405():
"""Routes without GET must still return 405 for HEAD."""
response = client.head("/submit")
assert response.status_code == 405
def test_get_still_works_after_head_support():
"""Adding HEAD must not break GET."""
response = client.get("/items/bar")
assert response.status_code == 200
assert response.json() == {"item_id": "bar"}
# ---------------------------------------------------------------------------
# OpenAPI: HEAD must NOT appear as a separate operation in the schema
# ---------------------------------------------------------------------------
def test_head_not_in_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200
schema = response.json()
for path, path_item in schema.get("paths", {}).items():
assert "head" not in path_item, (
f"HEAD should not be a documented OpenAPI operation, "
f"but found it at path '{path}'"
)
def test_get_present_in_openapi_schema():
response = client.get("/openapi.json")
schema = response.json()
assert "get" in schema["paths"]["/items/{item_id}"]
assert "get" in schema["paths"]["/ping"]