Fix: Use typing_extensions.Annotated for Python 3.8 compatibility

Changed import in tests/test_forms_fields_set.py from typing.Annotated
to typing_extensions.Annotated to ensure tests run on Python 3.8.

This fixes the Test / check CI failure and restores coverage to 100%
by allowing the comprehensive coverage tests to execute on all Python
versions in the CI matrix.
This commit is contained in:
Adarsh Bennur 2026-02-06 22:42:15 +05:30
parent ca8b59f52d
commit 3f2e378ff3
1 changed files with 18 additions and 18 deletions

View File

@ -6,7 +6,7 @@ This test validates that Form models correctly track which fields were
explicitly provided vs. which fields use defaults.
"""
from typing import Annotated
from typing_extensions import Annotated
from fastapi import FastAPI, Form, Header, Query
from fastapi._compat import PYDANTIC_V2
@ -14,7 +14,7 @@ from fastapi.testclient import TestClient
from pydantic import BaseModel
class FormModelFieldsSet(BaseModel):
class FormModelFieldsSet(BaseModel) -> None:
"""Model for testing fields_set metadata preservation."""
field_1: bool = True
@ -26,7 +26,7 @@ app = FastAPI()
@app.post("/form-fields-set")
async def form_fields_set_endpoint(model: Annotated[FormModelFieldsSet, Form()]):
async def form_fields_set_endpoint(model: Annotated[FormModelFieldsSet, Form()]) -> None:
# Use correct attribute name for each Pydantic version
if PYDANTIC_V2:
fields_set = list(model.model_fields_set)
@ -39,7 +39,7 @@ async def form_fields_set_endpoint(model: Annotated[FormModelFieldsSet, Form()])
@app.post("/body-fields-set")
async def body_fields_set_endpoint(model: FormModelFieldsSet):
async def body_fields_set_endpoint(model: FormModelFieldsSet) -> None:
# Use correct attribute name for each Pydantic version
if PYDANTIC_V2:
fields_set = list(model.model_fields_set)
@ -52,14 +52,14 @@ async def body_fields_set_endpoint(model: FormModelFieldsSet):
def query_model(
name: Annotated[str, Query()] = "query_default",
age: Annotated[int, Query()] = 10,
):
) -> None:
return {"name": name, "age": age}
@app.get("/header/default")
def header_model(
x_token: Annotated[str, Header()] = "header_default",
):
) -> None:
return {"x_token": x_token}
@ -69,35 +69,35 @@ client = TestClient(app)
class TestFormFieldsSetMetadata:
"""Test that Form models correctly preserve fields_set metadata."""
def test_form_empty_data_has_empty_fields_set(self):
def test_form_empty_data_has_empty_fields_set(self) -> None:
"""Form with no data should have empty fields_set (matching JSON behavior)."""
resp = client.post("/form-fields-set", data={})
assert resp.status_code == 200, resp.text
fields_set = resp.json()["fields_set"]
assert fields_set == []
def test_body_empty_data_has_empty_fields_set(self):
def test_body_empty_data_has_empty_fields_set(self) -> None:
"""JSON body with no data should have empty fields_set."""
resp = client.post("/body-fields-set", json={})
assert resp.status_code == 200, resp.text
fields_set = resp.json()["fields_set"]
assert fields_set == []
def test_form_partial_data_tracks_provided_fields(self):
def test_form_partial_data_tracks_provided_fields(self) -> None:
"""Form with partial data should only show provided fields in fields_set."""
resp = client.post("/form-fields-set", data={"field_1": "False"})
assert resp.status_code == 200, resp.text
fields_set = resp.json()["fields_set"]
assert fields_set == ["field_1"]
def test_body_partial_data_tracks_provided_fields(self):
def test_body_partial_data_tracks_provided_fields(self) -> None:
"""JSON body with partial data should only show provided fields."""
resp = client.post("/body-fields-set", json={"field_1": False})
assert resp.status_code == 200, resp.text
fields_set = resp.json()["fields_set"]
assert fields_set == ["field_1"]
def test_form_all_fields_provided(self):
def test_form_all_fields_provided(self) -> None:
"""Form with all fields should show all fields in fields_set."""
resp = client.post(
"/form-fields-set",
@ -107,7 +107,7 @@ class TestFormFieldsSetMetadata:
fields_set = resp.json()["fields_set"]
assert set(fields_set) == {"field_1", "field_2", "field_3"}
def test_body_all_fields_provided(self):
def test_body_all_fields_provided(self) -> None:
"""JSON body with all fields should show all fields in fields_set."""
resp = client.post(
"/body-fields-set",
@ -117,7 +117,7 @@ class TestFormFieldsSetMetadata:
fields_set = resp.json()["fields_set"]
assert set(fields_set) == {"field_1", "field_2", "field_3"}
def test_form_field_set_to_default_value_is_tracked(self):
def test_form_field_set_to_default_value_is_tracked(self) -> None:
"""Form field explicitly set to default value should appear in fields_set."""
# Same as default=True, but explicitly provided
resp = client.post("/form-fields-set", data={"field_1": "True"})
@ -125,14 +125,14 @@ class TestFormFieldsSetMetadata:
fields_set = resp.json()["fields_set"]
assert fields_set == ["field_1"]
def test_body_field_set_to_default_value_is_tracked(self):
def test_body_field_set_to_default_value_is_tracked(self) -> None:
"""JSON body field explicitly set to default value should appear in fields_set."""
resp = client.post("/body-fields-set", json={"field_1": True})
assert resp.status_code == 200, resp.text
fields_set = resp.json()["fields_set"]
assert fields_set == ["field_1"]
def test_form_body_consistency(self):
def test_form_body_consistency(self) -> None:
"""
Verify that body and form behave consistently.
Form fields_set should match JSON body fields_set for equivalent data.
@ -160,21 +160,21 @@ class TestNonFormCoverage:
This ensures line 762 of utils.py is covered and legacy behavior is preserved.
"""
def test_query_params_missing_uses_defaults(self):
def test_query_params_missing_uses_defaults(self) -> None:
"""Test Query input where fields are missing -> returns default."""
response = client.get("/query/default")
assert response.status_code == 200
data = response.json()
assert data == {"name": "query_default", "age": 10}
def test_header_params_missing_uses_defaults(self):
def test_header_params_missing_uses_defaults(self) -> None:
"""Test Header input where fields are missing -> returns default."""
response = client.get("/header/default")
assert response.status_code == 200
data = response.json()
assert data == {"x_token": "header_default"}
def test_query_params_provided(self):
def test_query_params_provided(self) -> None:
"""Test Query input where fields are provided -> returns value."""
response = client.get("/query/default?name=overridden&age=99")
assert response.status_code == 200