mirror of
https://github.com/fastapi/fastapi.git
synced 2026-02-25 19:29:45 -05:00
Update tests for passing empty str to Form
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from typing import Annotated, Any, Union
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
import pytest
|
||||
from dirty_equals import IsList, IsOneOf, IsPartialDict
|
||||
@@ -150,18 +150,55 @@ def test_nullable_required_missing(path: str):
|
||||
pytest.param(
|
||||
"/nullable-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty str is replaced with None, but then None gets dropped"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
"/model-nullable-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are not replaced with None for models"
|
||||
reason="Empty str is replaced with None even for required parameters"
|
||||
),
|
||||
),
|
||||
"/model-nullable-required",
|
||||
],
|
||||
)
|
||||
def test_nullable_required_pass_empty_str(path: str):
|
||||
def test_nullable_required_pass_empty_str_to_str_val(path: str):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
response = client.post(
|
||||
path,
|
||||
data={
|
||||
"int_val": "0", # Empty string would cause validation error (see below)
|
||||
"str_val": "",
|
||||
"list_val": "0", # Empty string would cause validation error (see below)
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 3, "Validator should be called for each field"
|
||||
assert mock_convert.call_args_list == [
|
||||
call("0"), # int_val
|
||||
call(""), # str_val
|
||||
call(["0"]), # list_val
|
||||
]
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"int_val": 0,
|
||||
"str_val": "",
|
||||
"list_val": [0],
|
||||
"fields_set": IsOneOf(
|
||||
None, IsList("int_val", "str_val", "list_val", check_order=False)
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
pytest.param(
|
||||
"/nullable-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty str is replaced with None even for required parameters"
|
||||
),
|
||||
),
|
||||
"/model-nullable-required",
|
||||
],
|
||||
)
|
||||
def test_nullable_required_pass_empty_str_to_int_val_and_list_val(path: str):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
@@ -170,26 +207,33 @@ def test_nullable_required_pass_empty_str(path: str):
|
||||
data={
|
||||
"int_val": "",
|
||||
"str_val": "",
|
||||
"list_val": "0", # Empty strings are not treated as null for lists. It's Ok
|
||||
"list_val": "",
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 3, "Validator should be called for each field"
|
||||
assert mock_convert.call_args_list == [
|
||||
(""), # int_val
|
||||
(""), # str_val
|
||||
(["0"]), # list_val
|
||||
call(""), # int_val
|
||||
call(""), # str_val
|
||||
call([""]), # list_val
|
||||
]
|
||||
assert response.status_code == 200, response.text # pragma: no cover
|
||||
assert response.json() == { # pragma: no cover
|
||||
"int_val": None,
|
||||
"str_val": None,
|
||||
"list_val": [0],
|
||||
"fields_set": IsOneOf(
|
||||
None, IsList("int_val", "str_val", "list_val", check_order=False)
|
||||
),
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"input": "",
|
||||
"loc": ["body", "int_val"],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"type": "int_parsing",
|
||||
},
|
||||
{
|
||||
"input": "",
|
||||
"loc": ["body", "list_val", 0],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"type": "int_parsing",
|
||||
},
|
||||
]
|
||||
}
|
||||
# TODO: Remove 'no cover' when the issue is fixed
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -336,21 +380,16 @@ def test_nullable_non_required_missing(path: str):
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
pytest.param(
|
||||
"/nullable-non-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty str is replaced with None, but then None gets dropped"
|
||||
),
|
||||
),
|
||||
"/nullable-non-required",
|
||||
pytest.param(
|
||||
"/model-nullable-non-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are not replaced with None for models"
|
||||
reason="Empty strings are not replaced with None for parameters declared as model"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nullable_non_required_pass_empty_str(path: str):
|
||||
def test_nullable_non_required_pass_empty_str_to_str_val_and_int_val(path: str):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
@@ -359,26 +398,63 @@ def test_nullable_non_required_pass_empty_str(path: str):
|
||||
data={
|
||||
"int_val": "",
|
||||
"str_val": "",
|
||||
"list_val": "0", # Empty strings are not treated as null for lists. It's Ok
|
||||
"list_val": "0", # Empty string would cause validation error (see below)
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 3, "Validator should be called for each field"
|
||||
assert mock_convert.call_count == 1, "Validator should be called for list_val only"
|
||||
assert mock_convert.call_args_list == [
|
||||
(""), # int_val
|
||||
(""), # str_val
|
||||
(["0"]), # list_val
|
||||
call(["0"]), # list_val
|
||||
]
|
||||
assert response.status_code == 200, response.text # pragma: no cover
|
||||
assert response.json() == { # pragma: no cover
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"int_val": None,
|
||||
"str_val": None,
|
||||
"list_val": [0],
|
||||
"fields_set": IsOneOf(
|
||||
None, IsList("int_val", "str_val", "list_val", check_order=False)
|
||||
),
|
||||
"fields_set": IsOneOf(None, IsList("list_val", check_order=False)),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
"/nullable-non-required",
|
||||
pytest.param(
|
||||
"/model-nullable-non-required",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are not replaced with None for parameters declared as model"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nullable_non_required_pass_empty_str_to_all(path: str):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
response = client.post(
|
||||
path,
|
||||
data={
|
||||
"int_val": "",
|
||||
"str_val": "",
|
||||
"list_val": "",
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 1, "Validator should be called for list_val only"
|
||||
assert mock_convert.call_args_list == [
|
||||
call([""]), # list_val
|
||||
]
|
||||
assert response.status_code == 422, response.text
|
||||
assert response.json() == {
|
||||
"detail": [
|
||||
{
|
||||
"input": "",
|
||||
"loc": ["body", "list_val", 0],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"type": "int_parsing",
|
||||
},
|
||||
]
|
||||
}
|
||||
# TODO: Remove 'no cover' when the issue is fixed
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -540,18 +616,20 @@ def test_nullable_with_non_null_default_missing(path: str):
|
||||
pytest.param(
|
||||
"/nullable-with-non-null-default",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty str is replaced with default value, not with None" # Is this correct ???
|
||||
reason="Empty strings are replaced with default values before validation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
"/model-nullable-with-non-null-default",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are not replaced with None for models"
|
||||
reason="Empty strings are not replaced with None for parameters declared as model"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nullable_with_non_null_default_pass_empty_str(path: str):
|
||||
def test_nullable_with_non_null_default_pass_empty_str_to_str_val_and_int_val(
|
||||
path: str,
|
||||
):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
@@ -560,24 +638,68 @@ def test_nullable_with_non_null_default_pass_empty_str(path: str):
|
||||
data={
|
||||
"int_val": "",
|
||||
"str_val": "",
|
||||
"list_val": "0", # Empty strings are not treated as null for lists. It's Ok
|
||||
"list_val": "0", # Empty string would cause validation error (see below)
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 3, "Validator should be called for each field"
|
||||
assert mock_convert.call_args_list == [
|
||||
(""), # int_val
|
||||
(""), # str_val
|
||||
(["0"]), # list_val
|
||||
assert mock_convert.call_count == 1, "Validator should be called for list_val only"
|
||||
assert mock_convert.call_args_list == [ # pragma: no cover
|
||||
call(["0"]), # list_val
|
||||
]
|
||||
assert response.status_code == 200, response.text # pragma: no cover
|
||||
assert response.json() == { # pragma: no cover
|
||||
"int_val": None,
|
||||
"str_val": None,
|
||||
"int_val": -1,
|
||||
"str_val": "default",
|
||||
"list_val": [0],
|
||||
"fields_set": IsOneOf(
|
||||
None, IsList("int_val", "str_val", "list_val", check_order=False)
|
||||
"fields_set": IsOneOf(None, IsList("list_val", check_order=False)),
|
||||
}
|
||||
# TODO: Remove 'no cover' when the issue is fixed
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path",
|
||||
[
|
||||
pytest.param(
|
||||
"/nullable-with-non-null-default",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are replaced with default values before validation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
"/model-nullable-with-non-null-default",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="Empty strings are not replaced with None for parameters declared as model"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nullable_with_non_null_default_pass_empty_str_to_all(path: str):
|
||||
client = TestClient(app)
|
||||
|
||||
with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert:
|
||||
response = client.post(
|
||||
path,
|
||||
data={
|
||||
"int_val": "",
|
||||
"str_val": "",
|
||||
"list_val": "",
|
||||
},
|
||||
)
|
||||
|
||||
assert mock_convert.call_count == 1, "Validator should be called for list_val only"
|
||||
assert mock_convert.call_args_list == [ # pragma: no cover
|
||||
call([""]), # list_val
|
||||
]
|
||||
assert response.status_code == 422, response.text # pragma: no cover
|
||||
assert response.json() == { # pragma: no cover
|
||||
"detail": [
|
||||
{
|
||||
"input": "",
|
||||
"loc": ["body", "list_val", 0],
|
||||
"msg": "Input should be a valid integer, unable to parse string as an integer",
|
||||
"type": "int_parsing",
|
||||
},
|
||||
]
|
||||
}
|
||||
# TODO: Remove 'no cover' when the issue is fixed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user