Skip to content

Commit f17f9d0

Browse files
committed
Unit test coverage for new property/response code
1 parent fbdbd21 commit f17f9d0

File tree

7 files changed

+311
-5
lines changed

7 files changed

+311
-5
lines changed

openapi_python_client/parser/properties/model_property.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ class ModelProperty(Property):
1717
description: str
1818
relative_imports: Set[str]
1919

20-
template: ClassVar[str] = "ref_property.pyi"
21-
# TODO: change to model_property.pyi
20+
template: ClassVar[str] = "model_property.pyi"
2221

2322
def get_type_string(self, no_optional: bool = False) -> str:
2423
""" Get a string representation of type that should be used when declaring this property """

openapi_python_client/parser/responses.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
__all__ = ["Response", "response_from_data"]
2+
13
from typing import Tuple, Union
24

35
import attr
@@ -63,7 +65,7 @@ def response_from_data(
6365
required=True,
6466
data=schema_data,
6567
schemas=schemas,
66-
parent_name=f"{parent_name}",
68+
parent_name=parent_name,
6769
)
6870

6971
if isinstance(prop, PropertyError):
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import pytest
2+
3+
from openapi_python_client.parser.errors import ValidationError
4+
from openapi_python_client.parser.properties.converter import convert, convert_chain
5+
6+
7+
def test_convert_none():
8+
assert convert("blah", None) is None
9+
10+
11+
def test_convert_bad_type():
12+
with pytest.raises(ValidationError):
13+
assert convert("blah", "blah")
14+
15+
16+
def test_convert_exception():
17+
with pytest.raises(ValidationError):
18+
assert convert("datetime.datetime", "blah")
19+
20+
21+
def test_convert_str():
22+
# This looks ugly, but it outputs in jinja as '\\"str\\"'
23+
# The extra escape of " is not necessary but the code is overly cautious
24+
assert convert("str", '"str"') == "'\\\\\"str\\\\\"'"
25+
26+
27+
def test_convert_datetime():
28+
assert convert("datetime.datetime", "2021-01-20") == "isoparse('2021-01-20')"
29+
30+
31+
def test_convert_date():
32+
assert convert("datetime.date", "2021-01-20") == "isoparse('2021-01-20').date()"
33+
34+
35+
def test_convert_chain_no_valid():
36+
with pytest.raises(ValidationError):
37+
convert_chain(("int",), "a")
38+
39+
40+
def test_convert_chain():
41+
assert convert_chain(("int", "bool"), "a")

tests/test_parser/test_properties/test_init.py

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
import openapi_python_client.schema as oai
44
from openapi_python_client.parser.errors import PropertyError, ValidationError
5-
from openapi_python_client.parser.properties import BooleanProperty, FloatProperty, IntProperty
5+
from openapi_python_client.parser.properties import (
6+
BooleanProperty,
7+
DateTimeProperty,
8+
FloatProperty,
9+
IntProperty,
10+
ModelProperty,
11+
StringProperty,
12+
)
13+
from openapi_python_client.parser.reference import Reference
614

715
MODULE_NAME = "openapi_python_client.parser.properties"
816

@@ -689,6 +697,25 @@ def test_property_from_data_array(self, mocker):
689697
data=data, name=name, required=required, schemas=schemas, parent_name="parent"
690698
)
691699

700+
def test_property_from_data_object(self, mocker):
701+
from openapi_python_client.parser.properties import Schemas, property_from_data
702+
703+
name = mocker.MagicMock()
704+
required = mocker.MagicMock()
705+
data = oai.Schema(
706+
type="object",
707+
)
708+
build_model_property = mocker.patch(f"{MODULE_NAME}.build_model_property")
709+
mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name)
710+
schemas = Schemas()
711+
712+
response = property_from_data(name=name, required=required, data=data, schemas=schemas, parent_name="parent")
713+
714+
assert response == build_model_property.return_value
715+
build_model_property.assert_called_once_with(
716+
data=data, name=name, required=required, schemas=schemas, parent_name="parent"
717+
)
718+
692719
def test_property_from_data_union(self, mocker):
693720
from openapi_python_client.parser.properties import Schemas, property_from_data
694721

@@ -1010,3 +1037,108 @@ def test_build_enums(mocker):
10101037

10111038
build_enum_property.assert_called()
10121039
build_model_property.assert_not_called()
1040+
1041+
1042+
def test_build_model_property():
1043+
from openapi_python_client.parser.properties import Schemas, build_model_property
1044+
1045+
data = oai.Schema.construct(
1046+
required=["req"],
1047+
title="MyModel",
1048+
properties={
1049+
"req": oai.Schema.construct(type="string"),
1050+
"opt": oai.Schema(type="string", format="date-time"),
1051+
},
1052+
description="A class called MyModel",
1053+
nullable=False,
1054+
)
1055+
schemas = Schemas(models={"OtherModel": None})
1056+
1057+
model, new_schemas = build_model_property(
1058+
data=data,
1059+
name="prop",
1060+
schemas=schemas,
1061+
required=True,
1062+
parent_name="parent",
1063+
)
1064+
1065+
assert new_schemas != schemas
1066+
assert new_schemas.models == {
1067+
"OtherModel": None,
1068+
"ParentMyModel": model,
1069+
}
1070+
assert model == ModelProperty(
1071+
name="prop",
1072+
required=True,
1073+
nullable=False,
1074+
default=None,
1075+
reference=Reference(class_name="ParentMyModel", module_name="parent_my_model"),
1076+
required_properties=[StringProperty(name="req", required=True, nullable=False, default=None)],
1077+
optional_properties=[DateTimeProperty(name="opt", required=True, nullable=False, default=None)],
1078+
description=data.description,
1079+
relative_imports={"from dateutil.parser import isoparse", "from typing import cast", "import datetime"},
1080+
)
1081+
1082+
1083+
def test_build_model_property_bad_prop():
1084+
from openapi_python_client.parser.properties import Schemas, build_model_property
1085+
1086+
data = oai.Schema(
1087+
properties={
1088+
"bad": oai.Schema(type="not_real"),
1089+
},
1090+
)
1091+
schemas = Schemas(models={"OtherModel": None})
1092+
1093+
err, new_schemas = build_model_property(
1094+
data=data,
1095+
name="prop",
1096+
schemas=schemas,
1097+
required=True,
1098+
parent_name=None,
1099+
)
1100+
1101+
assert new_schemas == schemas
1102+
assert err == PropertyError(detail="unknown type not_real", data=oai.Schema(type="not_real"))
1103+
1104+
1105+
def test_build_enum_property_conflict(mocker):
1106+
from openapi_python_client.parser.properties import Schemas, build_enum_property
1107+
1108+
data = oai.Schema()
1109+
schemas = Schemas(enums={"Existing": mocker.MagicMock()})
1110+
1111+
err, schemas = build_enum_property(
1112+
data=data, name="Existing", required=True, schemas=schemas, enum=[], parent_name=None
1113+
)
1114+
1115+
assert schemas == schemas
1116+
assert err == PropertyError(detail="Found conflicting enums named Existing with incompatible values.", data=data)
1117+
1118+
1119+
def test_build_enum_property_no_values():
1120+
from openapi_python_client.parser.properties import Schemas, build_enum_property
1121+
1122+
data = oai.Schema()
1123+
schemas = Schemas()
1124+
1125+
err, schemas = build_enum_property(
1126+
data=data, name="Existing", required=True, schemas=schemas, enum=[], parent_name=None
1127+
)
1128+
1129+
assert schemas == schemas
1130+
assert err == PropertyError(detail="No values provided for Enum", data=data)
1131+
1132+
1133+
def test_build_enum_property_bad_default():
1134+
from openapi_python_client.parser.properties import Schemas, build_enum_property
1135+
1136+
data = oai.Schema(default="B")
1137+
schemas = Schemas()
1138+
1139+
err, schemas = build_enum_property(
1140+
data=data, name="Existing", required=True, schemas=schemas, enum=["A"], parent_name=None
1141+
)
1142+
1143+
assert schemas == schemas
1144+
assert err == PropertyError(detail="B is an invalid default for enum Existing", data=data)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import pytest
2+
3+
4+
@pytest.mark.parametrize(
5+
"no_optional,nullable,required,expected",
6+
[
7+
(False, False, False, "Union[MyClass, Unset]"),
8+
(False, False, True, "MyClass"),
9+
(False, True, False, "Union[Optional[MyClass], Unset]"),
10+
(False, True, True, "Optional[MyClass]"),
11+
(True, False, False, "MyClass"),
12+
(True, False, True, "MyClass"),
13+
(True, True, False, "MyClass"),
14+
(True, True, True, "MyClass"),
15+
],
16+
)
17+
def test_get_type_string(no_optional, nullable, required, expected):
18+
from openapi_python_client.parser.properties import ModelProperty, Reference
19+
20+
prop = ModelProperty(
21+
name="prop",
22+
required=required,
23+
nullable=nullable,
24+
default=None,
25+
reference=Reference(class_name="MyClass", module_name="my_module"),
26+
description="",
27+
optional_properties=[],
28+
required_properties=[],
29+
relative_imports=set(),
30+
)
31+
32+
assert prop.get_type_string(no_optional=no_optional) == expected
33+
34+
35+
def test_get_imports():
36+
from openapi_python_client.parser.properties import ModelProperty, Reference
37+
38+
prop = ModelProperty(
39+
name="prop",
40+
required=False,
41+
nullable=True,
42+
default=None,
43+
reference=Reference(class_name="MyClass", module_name="my_module"),
44+
description="",
45+
optional_properties=[],
46+
required_properties=[],
47+
relative_imports=set(),
48+
)
49+
50+
assert prop.get_imports(prefix="..") == {
51+
"from typing import Optional",
52+
"from typing import Union",
53+
"from ..types import UNSET, Unset",
54+
"from ..models.my_module import MyClass",
55+
"from typing import Dict",
56+
"from typing import cast",
57+
}

tests/test_parser/test_responses.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,80 @@
11
import openapi_python_client.schema as oai
2+
from openapi_python_client.parser.errors import ParseError, PropertyError
3+
from openapi_python_client.parser.properties import NoneProperty, Schemas, StringProperty
24

35
MODULE_NAME = "openapi_python_client.parser.responses"
46

5-
# TODO: Test response_from_data
7+
8+
def test_response_from_data_no_content():
9+
from openapi_python_client.parser.responses import Response, response_from_data
10+
11+
response, schemas = response_from_data(
12+
status_code=200, data=oai.Response.construct(description=""), schemas=Schemas(), parent_name="parent"
13+
)
14+
15+
assert response == Response(
16+
status_code=200,
17+
prop=NoneProperty(name="response_200", default=None, nullable=False, required=True),
18+
source="None",
19+
)
20+
21+
22+
def test_response_from_data_unsupported_content_type():
23+
from openapi_python_client.parser.responses import response_from_data
24+
25+
data = oai.Response.construct(description="", content={"blah": None})
26+
response, schemas = response_from_data(status_code=200, data=data, schemas=Schemas(), parent_name="parent")
27+
28+
assert response == ParseError(data=data, detail="Unsupported content_type {'blah': None}")
29+
30+
31+
def test_response_from_data_no_content_schema():
32+
from openapi_python_client.parser.responses import Response, response_from_data
33+
34+
data = oai.Response.construct(description="", content={"application/json": oai.MediaType.construct()})
35+
response, schemas = response_from_data(status_code=200, data=data, schemas=Schemas(), parent_name="parent")
36+
37+
assert response == Response(
38+
status_code=200,
39+
prop=NoneProperty(name="response_200", default=None, nullable=False, required=True),
40+
source="None",
41+
)
42+
43+
44+
def test_response_from_data_property_error(mocker):
45+
from openapi_python_client.parser import responses
46+
47+
property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(PropertyError(), Schemas()))
48+
data = oai.Response.construct(
49+
description="", content={"application/json": oai.MediaType.construct(media_type_schema="something")}
50+
)
51+
response, schemas = responses.response_from_data(
52+
status_code=400, data=data, schemas=Schemas(), parent_name="parent"
53+
)
54+
55+
assert response == PropertyError()
56+
property_from_data.assert_called_once_with(
57+
name="response_400", required=True, data="something", schemas=Schemas(), parent_name="parent"
58+
)
59+
60+
61+
def test_response_from_data_property(mocker):
62+
from openapi_python_client.parser import responses
63+
64+
prop = StringProperty(name="prop", required=True, nullable=False, default=None)
65+
property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas()))
66+
data = oai.Response.construct(
67+
description="", content={"application/json": oai.MediaType.construct(media_type_schema="something")}
68+
)
69+
response, schemas = responses.response_from_data(
70+
status_code=400, data=data, schemas=Schemas(), parent_name="parent"
71+
)
72+
73+
assert response == responses.Response(
74+
status_code=400,
75+
prop=prop,
76+
source="response.json()",
77+
)
78+
property_from_data.assert_called_once_with(
79+
name="response_400", required=True, data="something", schemas=Schemas(), parent_name="parent"
80+
)

0 commit comments

Comments
 (0)