From f49f65aa1619efbfbdbb788a5ad97917c273e140 Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Fri, 6 Feb 2026 16:59:18 +0100 Subject: [PATCH] Add tests with passing empty string value --- .../test_body/test_nullable_and_defaults.py | 64 ++++++++----- .../test_cookie/test_nullable_and_defaults.py | 52 ++++++---- .../test_header/test_nullable_and_defaults.py | 96 +++++++++++++++++++ .../test_query/test_nullable_and_defaults.py | 57 +++++++---- 4 files changed, 210 insertions(+), 59 deletions(-) diff --git a/tests/test_request_params/test_body/test_nullable_and_defaults.py b/tests/test_request_params/test_body/test_nullable_and_defaults.py index ba1a39b20..e78811b22 100644 --- a/tests/test_request_params/test_body/test_nullable_and_defaults.py +++ b/tests/test_request_params/test_body/test_nullable_and_defaults.py @@ -261,7 +261,9 @@ def test_nullable_required_no_embed_missing(path: str): ), ], ) -def test_nullable_required_no_embed_pass_empty_dict(path: str, msg: str, error_type: str): +def test_nullable_required_no_embed_pass_empty_dict( + path: str, msg: str, error_type: str +): client = TestClient(app) response = client.post(path, json={}) assert response.status_code == 422 @@ -344,20 +346,25 @@ def test_nullable_required_no_embed_pass_null(path: str): "/model-nullable-required", ], ) -def test_nullable_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_required_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.post( - path, json={"int_val": "1", "str_val": "test", "list_val": ["1", "2"]} - ) + response = client.post(path, json=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ), @@ -368,6 +375,7 @@ def test_nullable_required_pass_value(path: str): ("path", "value"), [ ("/nullable-required-str", "test"), + ("/nullable-required-str", ""), ("/nullable-required-int", 1), ("/nullable-required-list", [1, 2]), ], @@ -685,20 +693,25 @@ def test_nullable_non_required_no_embed_pass_null(path: str): "/model-nullable-non-required", ], ) -def test_nullable_non_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_non_required_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.post( - path, json={"int_val": 1, "str_val": "test", "list_val": [1, 2]} - ) + response = client.post(path, json=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ), @@ -709,6 +722,7 @@ def test_nullable_non_required_pass_value(path: str): ("path", "value"), [ ("/nullable-non-required-str", "test"), + ("/nullable-non-required-str", ""), ("/nullable-non-required-int", 1), ("/nullable-non-required-list", [1, 2]), ], @@ -1033,20 +1047,25 @@ def test_nullable_with_non_null_default_no_embed_pass_null(path: str): "/model-nullable-with-non-null-default", ], ) -def test_nullable_with_non_null_default_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_with_non_null_default_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.post( - path, json={"int_val": "1", "str_val": "test", "list_val": ["1", "2"]} - ) + response = client.post(path, json=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ), @@ -1057,6 +1076,7 @@ def test_nullable_with_non_null_default_pass_value(path: str): ("path", "value"), [ ("/nullable-with-non-null-default-str", "test"), + ("/nullable-with-non-null-default-str", ""), ("/nullable-with-non-null-default-int", 1), ("/nullable-with-non-null-default-list", [1, 2]), ], diff --git a/tests/test_request_params/test_cookie/test_nullable_and_defaults.py b/tests/test_request_params/test_cookie/test_nullable_and_defaults.py index 88fa4f78d..142df0e40 100644 --- a/tests/test_request_params/test_cookie/test_nullable_and_defaults.py +++ b/tests/test_request_params/test_cookie/test_nullable_and_defaults.py @@ -130,19 +130,25 @@ def test_nullable_required_missing(path: str): "/model-nullable-required", ], ) -def test_nullable_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test"}, + {"int_val": "0", "str_val": ""}, + ], +) +def test_nullable_required_pass_value(path: str, values: dict[str, str]): client = TestClient(app) - client.cookies.set("int_val", "1") - client.cookies.set("str_val", "test") - + client.cookies.set("int_val", values["int_val"]) + client.cookies.set("str_val", values["str_val"]) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: response = client.get(path) assert mock_convert.call_count == 2, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", + "int_val": int(values["int_val"]), + "str_val": values["str_val"], "fields_set": IsOneOf(None, IsList("int_val", "str_val", check_order=False)), } @@ -255,10 +261,17 @@ def test_nullable_non_required_missing(path: str): "/model-nullable-non-required", ], ) -def test_nullable_non_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test"}, + {"int_val": "0", "str_val": ""}, + ], +) +def test_nullable_non_required_pass_value(path: str, values: dict[str, str]): client = TestClient(app) - client.cookies.set("int_val", "1") - client.cookies.set("str_val", "test") + client.cookies.set("int_val", values["int_val"]) + client.cookies.set("str_val", values["str_val"]) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: response = client.get(path) @@ -266,8 +279,8 @@ def test_nullable_non_required_pass_value(path: str): assert mock_convert.call_count == 2, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", + "int_val": int(values["int_val"]), + "str_val": values["str_val"], "fields_set": IsOneOf(None, IsList("int_val", "str_val", check_order=False)), } @@ -385,10 +398,17 @@ def test_nullable_with_non_null_default_missing(path: str): "/model-nullable-with-non-null-default", ], ) -def test_nullable_with_non_null_default_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test"}, + {"int_val": "0", "str_val": ""}, + ], +) +def test_nullable_with_non_null_default_pass_value(path: str, values: dict[str, str]): client = TestClient(app) - client.cookies.set("int_val", "1") - client.cookies.set("str_val", "test") + client.cookies.set("int_val", values["int_val"]) + client.cookies.set("str_val", values["str_val"]) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: response = client.get(path) @@ -396,7 +416,7 @@ def test_nullable_with_non_null_default_pass_value(path: str): assert mock_convert.call_count == 2, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", + "int_val": int(values["int_val"]), + "str_val": values["str_val"], "fields_set": IsOneOf(None, IsList("int_val", "str_val", check_order=False)), } diff --git a/tests/test_request_params/test_header/test_nullable_and_defaults.py b/tests/test_request_params/test_header/test_nullable_and_defaults.py index 4236d5fa9..a7bfbb6c2 100644 --- a/tests/test_request_params/test_header/test_nullable_and_defaults.py +++ b/tests/test_request_params/test_header/test_nullable_and_defaults.py @@ -195,6 +195,38 @@ def test_nullable_required_pass_value(path: str): } +@pytest.mark.parametrize( + "path", + [ + "/nullable-required", + "/model-nullable-required", + ], +) +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.get( + path, + headers=[ + ("int-val", "1"), + ("str-val", ""), + ("list-val", "1"), + ], + ) + + assert mock_convert.call_count == 3, "Validator should be called for each field" + assert response.status_code == 200, response.text + assert response.json() == { + "int_val": 1, + "str_val": "", + "list_val": [1], + "fields_set": IsOneOf( + None, IsList("int_val", "str_val", "list_val", check_order=False) + ), + } + + # ===================================================================================== # Nullable with default=None @@ -356,6 +388,38 @@ def test_nullable_non_required_pass_value(path: str): } +@pytest.mark.parametrize( + "path", + [ + "/nullable-non-required", + "/model-nullable-non-required", + ], +) +def test_nullable_non_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.get( + path, + headers=[ + ("int-val", "1"), + ("str-val", ""), + ("list-val", "1"), + ], + ) + + assert mock_convert.call_count == 3, "Validator should be called for each field" + assert response.status_code == 200, response.text + assert response.json() == { + "int_val": 1, + "str_val": "", + "list_val": [1], + "fields_set": IsOneOf( + None, IsList("int_val", "str_val", "list_val", check_order=False) + ), + } + + # ===================================================================================== # Nullable with not-None default @@ -527,3 +591,35 @@ def test_nullable_with_non_null_default_pass_value(path: str): None, IsList("int_val", "str_val", "list_val", check_order=False) ), } + + +@pytest.mark.parametrize( + "path", + [ + "/nullable-with-non-null-default", + "/model-nullable-with-non-null-default", + ], +) +def test_nullable_with_non_null_default_pass_empty_str_to_str_val(path: str): + client = TestClient(app) + + with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: + response = client.get( + path, + headers=[ + ("int-val", "1"), + ("str-val", ""), + ("list-val", "1"), + ], + ) + + assert mock_convert.call_count == 3, "Validator should be called for each field" + assert response.status_code == 200, response.text + assert response.json() == { + "int_val": 1, + "str_val": "", + "list_val": [1], + "fields_set": IsOneOf( + None, IsList("int_val", "str_val", "list_val", check_order=False) + ), + } diff --git a/tests/test_request_params/test_query/test_nullable_and_defaults.py b/tests/test_request_params/test_query/test_nullable_and_defaults.py index 577e969f3..47cb67cb4 100644 --- a/tests/test_request_params/test_query/test_nullable_and_defaults.py +++ b/tests/test_request_params/test_query/test_nullable_and_defaults.py @@ -155,20 +155,25 @@ def test_nullable_required_missing(path: str): "/model-nullable-required", ], ) -def test_nullable_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_required_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.get( - path, params={"int_val": "1", "str_val": "test", "list_val": ["1", "2"]} - ) + response = client.get(path, params=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ), @@ -303,20 +308,25 @@ def test_nullable_non_required_missing(path: str): "/model-nullable-non-required", ], ) -def test_nullable_non_required_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_non_required_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.get( - path, params={"int_val": "1", "str_val": "test", "list_val": ["1", "2"]} - ) + response = client.get(path, params=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ), @@ -463,20 +473,25 @@ def test_nullable_with_non_null_default_missing(path: str): "/model-nullable-with-non-null-default", ], ) -def test_nullable_with_non_null_default_pass_value(path: str): +@pytest.mark.parametrize( + "values", + [ + {"int_val": "1", "str_val": "test", "list_val": ["1", "2"]}, + {"int_val": "0", "str_val": "", "list_val": ["0"]}, + ], +) +def test_nullable_with_non_null_default_pass_value(path: str, values: dict[str, Any]): client = TestClient(app) with patch(f"{__name__}.convert", Mock(wraps=convert)) as mock_convert: - response = client.get( - path, params={"int_val": "1", "str_val": "test", "list_val": ["1", "2"]} - ) + response = client.get(path, params=values) assert mock_convert.call_count == 3, "Validator should be called for each field" assert response.status_code == 200, response.text assert response.json() == { - "int_val": 1, - "str_val": "test", - "list_val": [1, 2], + "int_val": int(values["int_val"]), + "str_val": values["str_val"], + "list_val": [int(v) for v in values["list_val"]], "fields_set": IsOneOf( None, IsList("int_val", "str_val", "list_val", check_order=False) ),