Skip to content

Commit e57aa39

Browse files
committed
misc improvements + remove redundant unit tests
1 parent b7d34a7 commit e57aa39

File tree

9 files changed

+33172
-372
lines changed

9 files changed

+33172
-372
lines changed

Diff for: end_to_end_tests/end_to_end_test_helpers.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,13 @@ def inline_spec_should_fail(
156156
) -> Result:
157157
"""Asserts that the generator could not process the spec.
158158
159-
Returns the full output.
159+
Returns the command result, which could include stdout data or an exception.
160160
"""
161161
with generate_client_from_inline_spec(
162162
openapi_spec, extra_args, filename_suffix, config, add_missing_sections, raise_on_error=False
163163
) as generated_client:
164164
assert generated_client.generator_result.exit_code != 0
165-
return generated_client.generator_result.stdout
165+
return generated_client.generator_result
166166

167167

168168
def inline_spec_should_cause_warnings(
@@ -198,6 +198,7 @@ def with_generated_client_fixture(
198198
def _decorator(cls):
199199
def generated_client(self):
200200
with generate_client_from_inline_spec(openapi_spec, extra_args=extra_args, config=config) as g:
201+
print(g.generator_result.stdout) # so we'll see the output if a test failed
201202
yield g
202203

203204
setattr(cls, name, pytest.fixture(scope="class")(generated_client))

Diff for: end_to_end_tests/generated_code_live_tests/test_defaults.py

+75-20
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
stringProp: {"type": "string", "default": "a"}
2323
numberProp: {"type": "number", "default": 1.5}
2424
intProp: {"type": "integer", "default": 2}
25-
noneProp: {"type": "null", "default": null}
25+
dateProp: {"type": "string", "format": "date", "default": "2024-01-02"}
26+
dateTimeProp: {"type": "string", "format": "date-time", "default": "2024-01-02T03:04:05Z"}
27+
uuidProp: {"type": "string", "format": "uuid", "default": "07EF8B4D-AA09-4FFA-898D-C710796AFF41"}
2628
anyPropWithString: {"default": "b"}
2729
anyPropWithInt: {"default": 3}
2830
booleanWithStringTrue1: {"type": "boolean", "default": "True"}
@@ -32,17 +34,22 @@
3234
intWithStringValue: {"type": "integer", "default": "4"}
3335
numberWithIntValue: {"type": "number", "default": 5}
3436
numberWithStringValue: {"type": "number", "default": "5.5"}
35-
noneWithStringValue: {"type": "null", "default": "None"}
37+
stringWithNumberValue: {"type": "string", "default": 6}
3638
""")
3739
@with_generated_code_imports(".models.MyModel")
38-
class TestDefaultValues:
39-
def test_defaults_in_initializer(self, MyModel, generated_client):
40+
class TestSimpleDefaults:
41+
# Note, the null/None type is not covered here due to a known bug:
42+
# https://github.com/openapi-generators/openapi-python-client/issues/1162
43+
def test_defaults_in_initializer(self, MyModel):
4044
instance = MyModel()
4145
assert instance == MyModel(
4246
boolean_prop=True,
4347
string_prop="a",
4448
number_prop=1.5,
4549
int_prop=2,
50+
date_prop=datetime.date(2024, 1, 2),
51+
date_time_prop=datetime.datetime(2024, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc),
52+
uuid_prop=uuid.UUID("07EF8B4D-AA09-4FFA-898D-C710796AFF41"),
4653
any_prop_with_string="b",
4754
any_prop_with_int=3,
4855
boolean_with_string_true_1=True,
@@ -52,9 +59,31 @@ def test_defaults_in_initializer(self, MyModel, generated_client):
5259
int_with_string_value=4,
5360
number_with_int_value=5,
5461
number_with_string_value=5.5,
62+
string_with_number_value="6",
5563
)
56-
# Note, currently the default for a None property does not work as expected--
57-
# the initializer will default it to UNSET rather than None.
64+
65+
66+
67+
@with_generated_client_fixture(
68+
"""
69+
components:
70+
schemas:
71+
MyEnum:
72+
type: string
73+
enum: ["a", "b"]
74+
MyModel:
75+
type: object
76+
properties:
77+
enumProp:
78+
allOf:
79+
- $ref: "#/components/schemas/MyEnum"
80+
default: "a"
81+
82+
""")
83+
@with_generated_code_imports(".models.MyEnum", ".models.MyModel")
84+
class TestEnumDefaults:
85+
def test_enum_default(self, MyEnum, MyModel):
86+
assert MyModel().enum_prop == MyEnum.A
5887

5988

6089
class TestInvalidDefaultValues:
@@ -79,20 +108,46 @@ def warnings(self):
79108
WithBadFloatAsOther:
80109
properties:
81110
badInt: {"type": "number", "default": true}
111+
WithBadDateAsString:
112+
properties:
113+
badDate: {"type": "string", "format": "date", "default": "xxx"}
114+
WithBadDateAsOther:
115+
properties:
116+
badDate: {"type": "string", "format": "date", "default": 3}
117+
WithBadDateTimeAsString:
118+
properties:
119+
badDate: {"type": "string", "format": "date-time", "default": "xxx"}
120+
WithBadDateTimeAsOther:
121+
properties:
122+
badDate: {"type": "string", "format": "date-time", "default": 3}
123+
WithBadUuidAsString:
124+
properties:
125+
badUuid: {"type": "string", "format": "uuid", "default": "xxx"}
126+
WithBadUuidAsOther:
127+
properties:
128+
badUuid: {"type": "string", "format": "uuid", "default": 3}
129+
WithBadEnum:
130+
properties:
131+
badEnum: {"type": "string", "enum": ["a", "b"], "default": "c"}
82132
"""
83133
)
84134

85-
def test_bad_boolean(self, warnings):
86-
assert_bad_schema_warning(warnings, "WithBadBoolean", "Invalid boolean value")
87-
88-
def test_bad_int_as_string(self, warnings):
89-
assert_bad_schema_warning(warnings, "WithBadIntAsString", "Invalid int value")
90-
91-
def test_bad_int_as_other(self, warnings):
92-
assert_bad_schema_warning(warnings, "WithBadIntAsOther", "Invalid int value")
93-
94-
def test_bad_float_as_string(self, warnings):
95-
assert_bad_schema_warning(warnings, "WithBadFloatAsString", "Invalid float value")
96-
97-
def test_bad_float_as_other(self, warnings):
98-
assert_bad_schema_warning(warnings, "WithBadFloatAsOther", "Cannot convert True to a float")
135+
@pytest.mark.parametrize(
136+
("model_name", "message"),
137+
[
138+
("WithBadBoolean", "Invalid boolean value"),
139+
("WithBadIntAsString", "Invalid int value"),
140+
("WithBadIntAsOther", "Invalid int value"),
141+
("WithBadFloatAsString", "Invalid float value"),
142+
("WithBadFloatAsOther", "Cannot convert True to a float"),
143+
("WithBadDateAsString", "Invalid date"),
144+
("WithBadDateAsOther", "Cannot convert 3 to a date"),
145+
("WithBadDateTimeAsString", "Invalid datetime"),
146+
("WithBadDateTimeAsOther", "Cannot convert 3 to a datetime"),
147+
("WithBadUuidAsString", "Invalid UUID value"),
148+
("WithBadUuidAsOther", "Invalid UUID value"),
149+
("WithBadEnum", "Value c is not valid for enum"),
150+
]
151+
)
152+
def test_bad_default_warning(self, model_name, message, warnings):
153+
assert_bad_schema_warning(warnings, model_name, message)

Diff for: end_to_end_tests/generated_code_live_tests/test_enum_and_const.py

+56-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
assert_bad_schema_warning,
66
assert_model_decode_encode,
77
inline_spec_should_cause_warnings,
8+
inline_spec_should_fail,
89
with_generated_code_import,
910
with_generated_client_fixture,
1011
with_generated_code_imports,
@@ -17,10 +18,15 @@
1718
schemas:
1819
MyEnum:
1920
type: string
20-
enum: ["a", "B"]
21+
enum: ["a", "B", "~weirdstring"]
2122
MyIntEnum:
2223
type: integer
23-
enum: [2, 3]
24+
enum: [2, 3, -4]
25+
MyEnumIncludingNull:
26+
type: ["string", "null"]
27+
enum: ["a", "b", null]
28+
MyNullOnlyEnum:
29+
enum: [null]
2430
MyModel:
2531
properties:
2632
enumProp: {"$ref": "#/components/schemas/MyEnum"}
@@ -29,24 +35,44 @@
2935
oneOf:
3036
- {"$ref": "#/components/schemas/MyEnum"}
3137
- type: "null"
38+
enumIncludingNullProp: {"$ref": "#/components/schemas/MyEnumIncludingNull"}
39+
nullOnlyEnumProp: {"$ref": "#/components/schemas/MyNullOnlyEnum"}
40+
inlineEnumProp:
41+
type: string
42+
enum: ["a", "b"]
3243
""")
33-
@with_generated_code_imports(".models.MyEnum", ".models.MyIntEnum", ".models.MyModel")
44+
@with_generated_code_imports(
45+
".models.MyEnum",
46+
".models.MyIntEnum",
47+
".models.MyEnumIncludingNullType1", # see comment in test_nullable_enum_prop
48+
".models.MyModel",
49+
".models.MyModelInlineEnumProp",
50+
)
3451
class TestEnumClasses:
3552
def test_enum_classes(self, MyEnum, MyIntEnum):
3653
assert MyEnum.A == MyEnum("a")
3754
assert MyEnum.B == MyEnum("B")
55+
assert MyEnum.VALUE_2 == MyEnum("~weirdstring")
3856
assert MyIntEnum.VALUE_2 == MyIntEnum(2)
3957
assert MyIntEnum.VALUE_3 == MyIntEnum(3)
58+
assert MyIntEnum.VALUE_NEGATIVE_4 == MyIntEnum(-4)
4059

41-
def test_enum_prop(self, MyModel, MyEnum, MyIntEnum):
60+
def test_enum_prop(self, MyModel, MyEnum, MyIntEnum, MyModelInlineEnumProp):
4261
assert_model_decode_encode(MyModel, {"enumProp": "B"}, MyModel(enum_prop=MyEnum.B))
4362
assert_model_decode_encode(MyModel, {"intEnumProp": 2}, MyModel(int_enum_prop=MyIntEnum.VALUE_2))
63+
assert_model_decode_encode(
64+
MyModel,
65+
{"inlineEnumProp": "a"},
66+
MyModel(inline_enum_prop=MyModelInlineEnumProp.A),
67+
)
4468

45-
def test_enum_prop_type(self, MyModel, MyEnum, MyIntEnum):
69+
def test_enum_prop_type(self, MyModel, MyEnum):
70+
# Just verifying that it's not using a literal vaue
4671
assert isinstance(MyModel.from_dict({"enumProp": "B"}).enum_prop, MyEnum)
47-
assert isinstance(MyModel.from_dict({"intEnumProp": 2}).int_enum_prop, MyIntEnum)
4872

49-
def test_nullable_enum_prop(self, MyModel, MyEnum):
73+
def test_nullable_enum_prop(self, MyModel, MyEnum, MyEnumIncludingNullType1):
74+
# Note, MyEnumIncludingNullType1 should be named just MyEnumIncludingNull -
75+
# known bug: https://github.com/openapi-generators/openapi-python-client/issues/1120
5076
assert_model_decode_encode(
5177
MyModel,
5278
{"nullableEnumProp": "B"},
@@ -57,6 +83,17 @@ def test_nullable_enum_prop(self, MyModel, MyEnum):
5783
{"nullableEnumProp": None},
5884
MyModel(nullable_enum_prop=None),
5985
)
86+
assert_model_decode_encode(
87+
MyModel,
88+
{"enumIncludingNullProp": "a"},
89+
MyModel(enum_including_null_prop=MyEnumIncludingNullType1.A),
90+
)
91+
assert_model_decode_encode(
92+
MyModel,
93+
{"enumIncludingNullProp": None},
94+
MyModel(enum_including_null_prop=None),
95+
)
96+
assert_model_decode_encode(MyModel, {"nullOnlyEnumProp": None}, MyModel(null_only_enum_prop=None))
6097

6198
def test_invalid_values(self, MyModel):
6299
with pytest.raises(ValueError):
@@ -202,3 +239,15 @@ def test_enum_unsupported_type(self, warnings):
202239

203240
def test_const_default_not_matching(self, warnings):
204241
assert_bad_schema_warning(warnings, "DefaultNotMatchingConst", "Invalid value for const")
242+
243+
def test_enum_duplicate_values(self):
244+
# This one currently causes a full generator failure rather than a warning
245+
result = inline_spec_should_fail(
246+
"""
247+
components:
248+
schemas:
249+
WithDuplicateValues:
250+
enum: ["x", "x"]
251+
"""
252+
)
253+
assert "Duplicate key X in enum" in str(result.exception)

Diff for: end_to_end_tests/generated_code_live_tests/test_property_encoding.py

+5
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ def test_decode_error_not_object(self, bad_data, MyModel):
133133
dateProp: {"type": "string", "format": "date"}
134134
dateTimeProp: {"type": "string", "format": "date-time"}
135135
uuidProp: {"type": "string", "format": "uuid"}
136+
unknownFormatProp: {"type": "string", "format": "weird"}
136137
""")
137138
@with_generated_code_imports(".models.MyModel")
138139
class TestSpecialStringFormats:
@@ -150,3 +151,7 @@ def test_uuid(self, MyModel):
150151
uuid_value = uuid.uuid1()
151152
json_data = {"uuidProp": str(uuid_value)}
152153
assert_model_decode_encode(MyModel, json_data, MyModel(uuid_prop=uuid_value))
154+
155+
def test_unknown_format(self, MyModel):
156+
json_data = {"unknownFormatProp": "whatever"}
157+
assert_model_decode_encode(MyModel, json_data, MyModel(unknown_format_prop="whatever"))

0 commit comments

Comments
 (0)