def test_swagger_document_invalid_schema(self, mocker): Schemas = mocker.patch(f"{MODULE_NAME}.Schemas") config = mocker.MagicMock() in_dict = {"swagger": "2.0"} from openapi_python_client.parser.openapi import GeneratorData generator_data = GeneratorData.from_dict(in_dict, config=config) assert generator_data == GeneratorError( header="Failed to parse OpenAPI document", detail=( "You may be trying to use a Swagger document; this is not supported by this project.\n\n" "3 validation errors for OpenAPI\n" "info\n" " field required (type=value_error.missing)\n" "paths\n" " field required (type=value_error.missing)\n" "openapi\n" " field required (type=value_error.missing)" ), ) Schemas.build.assert_not_called() Schemas.assert_not_called()
def test_response_from_data_property_error(mocker): from openapi_python_client.parser import responses property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(PropertyError(), Schemas())) data = oai.Response.construct( description="", content={ "application/json": oai.MediaType.construct(media_type_schema="something") }) config = MagicMock() response, schemas = responses.response_from_data(status_code=400, data=data, schemas=Schemas(), parent_name="parent", config=config) assert response == PropertyError() property_from_data.assert_called_once_with(name="response_400", required=True, data="something", schemas=Schemas(), parent_name="parent", config=config)
def test_property_from_data_union(self, mocker): name = mocker.MagicMock() required = mocker.MagicMock() data = oai.Schema( anyOf=[{"type": "number", "default": "0.0"}], oneOf=[ {"type": "integer", "default": "0"}, ], ) UnionProperty = mocker.patch(f"{MODULE_NAME}.UnionProperty") FloatProperty = mocker.patch(f"{MODULE_NAME}.FloatProperty") IntProperty = mocker.patch(f"{MODULE_NAME}.IntProperty") mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name) from openapi_python_client.parser.properties import Schemas, property_from_data p, s = property_from_data( name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=MagicMock() ) FloatProperty.assert_called_once_with(name=name, required=required, default=0.0, nullable=False) IntProperty.assert_called_once_with(name=name, required=required, default=0, nullable=False) UnionProperty.assert_called_once_with( name=name, required=required, default=None, inner_properties=[FloatProperty.return_value, IntProperty.return_value], nullable=False, ) assert p == UnionProperty.return_value assert s == Schemas()
def test_property_from_data_union( self, union_property_factory, date_time_property_factory, string_property_factory ): from openapi_python_client.parser.properties import Schemas, property_from_data name = "union_prop" required = True data = oai.Schema( anyOf=[{"type": "string", "default": "a"}], oneOf=[ {"type": "string", "format": "date-time"}, ], ) expected = union_property_factory( name=name, required=required, inner_properties=[ string_property_factory(name=f"{name}_type_0", default="'a'"), date_time_property_factory(name=f"{name}_type_1"), ], ) p, s = property_from_data( name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=MagicMock() ) assert p == expected assert s == Schemas()
def test_skips_references_and_keeps_going(self, mocker): from openapi_python_client.parser.properties import Schemas, build_schemas from openapi_python_client.schema import Reference, Schema components = { "a_ref": Reference.construct(), "a_schema": Schema.construct() } update_schemas_with_data = mocker.patch( f"{MODULE_NAME}.update_schemas_with_data") parse_reference_path = mocker.patch( f"{MODULE_NAME}.parse_reference_path") config = Config() result = build_schemas(components=components, schemas=Schemas(), config=config) # Should not even try to parse a path for the Reference parse_reference_path.assert_called_once_with( "#/components/schemas/a_schema") update_schemas_with_data.assert_called_once_with( ref_path=parse_reference_path.return_value, config=config, data=components["a_schema"], schemas=Schemas(errors=[ PropertyError(detail="Reference schemas are not supported.", data=components["a_ref"]) ]), ) assert result == update_schemas_with_data.return_value
def test_records_bad_uris_and_keeps_going(self, mocker): from openapi_python_client.parser.properties import Schemas, build_schemas from openapi_python_client.schema import Schema components = { "first": Schema.construct(), "second": Schema.construct() } update_schemas_with_data = mocker.patch( f"{MODULE_NAME}.update_schemas_with_data") parse_reference_path = mocker.patch( f"{MODULE_NAME}.parse_reference_path", side_effect=[PropertyError(detail="some details"), "a_path"]) config = Config() result = build_schemas(components=components, schemas=Schemas(), config=config) parse_reference_path.assert_has_calls([ call("#/components/schemas/first"), call("#/components/schemas/second"), ]) update_schemas_with_data.assert_called_once_with( ref_path="a_path", config=config, data=components["second"], schemas=Schemas(errors=[ PropertyError(detail="some details", data=components["first"]) ]), ) assert result == update_schemas_with_data.return_value
def test_retries_failing_properties_while_making_progress(self, mocker): from openapi_python_client.parser.properties import Schemas, build_schemas from openapi_python_client.schema import Schema components = { "first": Schema.construct(), "second": Schema.construct() } update_schemas_with_data = mocker.patch( f"{MODULE_NAME}.update_schemas_with_data", side_effect=[PropertyError(), Schemas(), PropertyError()]) parse_reference_path = mocker.patch( f"{MODULE_NAME}.parse_reference_path") config = Config() result = build_schemas(components=components, schemas=Schemas(), config=config) parse_reference_path.assert_has_calls([ call("#/components/schemas/first"), call("#/components/schemas/second"), call("#/components/schemas/first"), ]) assert update_schemas_with_data.call_count == 3 assert result.errors == [PropertyError()]
def test_response_from_data_property(mocker): from openapi_python_client.parser import responses prop = StringProperty(name="prop", required=True, nullable=False, default=None) property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) data = oai.Response.construct( description="", content={ "application/json": oai.MediaType.construct(media_type_schema="something") }) response, schemas = responses.response_from_data(status_code=400, data=data, schemas=Schemas(), parent_name="parent") assert response == responses.Response( status_code=400, prop=prop, source="response.json()", ) property_from_data.assert_called_once_with(name="response_400", required=True, data="something", schemas=Schemas(), parent_name="parent")
def test_parse_multipart_body_existing_schema(self, mocker, model_property_factory): from openapi_python_client.parser.openapi import Endpoint, Schemas from openapi_python_client.parser.properties import Class class_info = Class(name="class_name", module_name="module_name") prop_before = model_property_factory(class_info=class_info, is_multipart_body=False) schemas_before = Schemas(classes_by_name={class_info.name: prop_before}) schema = mocker.MagicMock() body = oai.RequestBody.construct( content={"multipart/form-data": oai.MediaType.construct(media_type_schema=schema)} ) config = MagicMock() property_from_data = mocker.patch( f"{MODULE_NAME}.property_from_data", return_value=(prop_before, schemas_before) ) result = Endpoint.parse_multipart_body(body=body, schemas=schemas_before, parent_name="parent", config=config) property_from_data.assert_called_once_with( name="multipart_data", required=True, data=schema, schemas=schemas_before, parent_name="parent", config=config, ) prop_after = model_property_factory(class_info=class_info, is_multipart_body=True) schemas_after = Schemas(classes_by_name={class_info.name: prop_after}) assert result == (prop_after, schemas_after)
def test_property_from_data_unsupported_type(self, mocker): name = mocker.MagicMock() required = mocker.MagicMock() data = oai.Schema.construct(type=mocker.MagicMock()) from openapi_python_client.parser.errors import PropertyError from openapi_python_client.parser.properties import Schemas, property_from_data assert property_from_data(name=name, required=required, data=data, schemas=Schemas(), parent_name="parent") == ( PropertyError(data=data, detail=f"unknown type {data.type}"), Schemas(), )
def test_property_from_data_ref_enum_with_invalid_default( self, enum_property_factory): from openapi_python_client.parser.properties import Class, Schemas, property_from_data name = "some_enum" data = oai.Schema.construct( default="x", allOf=[oai.Reference.construct(ref="#/components/schemas/MyEnum")]) existing_enum = enum_property_factory( name="an_enum", default="MyEnum.A", values={ "A": "a", "B": "b" }, class_info=Class(name="MyEnum", module_name="my_enum"), python_name="an_enum", ) schemas = Schemas( classes_by_reference={"/components/schemas/MyEnum": existing_enum}) prop, new_schemas = property_from_data(name=name, required=False, data=data, schemas=schemas, parent_name="", config=Config()) assert schemas == new_schemas assert prop == PropertyError( data=data, detail="x is an invalid default for enum MyEnum")
def test_mixed_requirements( self, model_property_factory, first_nullable, second_nullable, first_required, second_required ): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct( allOf=[oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second")] ) schemas = Schemas( classes_by_reference={ "/First": model_property_factory( optional_properties=[string_property(required=first_required, nullable=first_nullable)] ), "/Second": model_property_factory( optional_properties=[string_property(required=second_required, nullable=second_nullable)] ), } ) result = _process_properties(data=data, schemas=schemas, class_name="", config=MagicMock()) nullable = first_nullable and second_nullable required = first_required or second_required expected_prop = string_property( nullable=nullable, required=required, ) if nullable or not required: assert result.optional_props == [expected_prop] else: assert result.required_props == [expected_prop]
def test_property_from_data_ref_enum(self, enum_property_factory): from openapi_python_client.parser.properties import Class, Schemas, property_from_data name = "some_enum" data = oai.Reference.construct(ref="#/components/schemas/MyEnum") existing_enum = enum_property_factory( name="an_enum", required=False, values={"A": "a"}, class_info=Class(name="MyEnum", module_name="my_enum"), ) schemas = Schemas( classes_by_reference={"/components/schemas/MyEnum": existing_enum}) prop, new_schemas = property_from_data(name=name, required=False, data=data, schemas=schemas, parent_name="", config=Config()) assert prop == enum_property_factory( name="some_enum", required=False, values={"A": "a"}, class_info=Class(name="MyEnum", module_name="my_enum"), ) assert schemas == new_schemas
def test_property_from_data_simple_types(self, openapi_type, prop_type, python_type): from openapi_python_client.parser.properties import Schemas, property_from_data name = "test_prop" required = True data = oai.Schema.construct(type=openapi_type, default=1) schemas = Schemas() p, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) assert p == prop_type(name=name, required=required, default=python_type(data.default), nullable=False) assert new_schemas == schemas # Test nullable values data.default = 0 data.nullable = True p, _ = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) assert p == prop_type(name=name, required=required, default=python_type(data.default), nullable=True) # Test bad default value data.default = "a" p, _ = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) assert python_type is bool or isinstance(p, PropertyError)
def test_property_from_data_str_enum(self, mocker): from openapi_python_client.parser.properties import Class, EnumProperty from openapi_python_client.schema import Schema data = Schema(title="AnEnum", enum=["A", "B", "C"], nullable=False, default="B") name = "my_enum" required = True from openapi_python_client.parser.properties import Schemas, property_from_data schemas = Schemas(classes_by_name={"AnEnum": mocker.MagicMock()}) prop, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) assert prop == EnumProperty( name="my_enum", required=True, nullable=False, values={"A": "A", "B": "B", "C": "C"}, class_info=Class(name="ParentAnEnum", module_name="parent_an_enum"), value_type=str, default="ParentAnEnum.B", ) assert schemas != new_schemas, "Provided Schemas was mutated" assert new_schemas.classes_by_name == { "AnEnum": schemas.classes_by_name["AnEnum"], "ParentAnEnum": prop, }
def test_property_from_data_union_of_one_element(self, mocker, model_property_factory): from openapi_python_client.parser.properties import Class, ModelProperty, Schemas, property_from_data name = "new_name" required = False class_name = "MyModel" nullable = True existing_model = model_property_factory() schemas = Schemas( classes_by_reference={f"/{class_name}": existing_model}) data = oai.Schema.construct( allOf=[oai.Reference.construct(ref=f"#/{class_name}")], nullable=nullable, ) build_union_property = mocker.patch( f"{MODULE_NAME}.build_union_property") prop, schemas = property_from_data(name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config()) assert prop == attr.evolve(existing_model, name=name, required=required, nullable=nullable, python_name=name) build_union_property.assert_not_called()
def test_allof_string_enum_and_string(self, model_property_factory, enum_property_factory, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct(allOf=[ oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second") ]) enum_property = enum_property_factory( required=False, nullable=True, values={"foo": "foo"}, ) schemas = Schemas( classes_by_reference={ "/First": model_property_factory(optional_properties=[enum_property]), "/Second": model_property_factory(optional_properties=[ string_property_factory(required=False, nullable=True) ]), }) result = _process_properties(data=data, schemas=schemas, class_name="", config=Config()) assert result.optional_props[0] == enum_property
def test_direct_properties_non_ref(self, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct(allOf=[ oai.Schema.construct( required=["first"], properties={ "first": oai.Schema.construct(type="string"), "second": oai.Schema.construct(type="string"), }, ) ]) schemas = Schemas() result = _process_properties(data=data, schemas=schemas, class_name="", config=MagicMock()) assert result.optional_props == [ string_property_factory(name="second", required=False, nullable=False) ] assert result.required_props == [ string_property_factory(name="first", required=True, nullable=False) ]
def test_property_from_data_str_enum_with_null(self, enum_property_factory): from openapi_python_client.parser.properties import Class, Schemas, property_from_data from openapi_python_client.schema import Schema existing = enum_property_factory() data = Schema(title="AnEnum", enum=["A", "B", "C", None], nullable=False, default="B") name = "my_enum" required = True schemas = Schemas(classes_by_name={"AnEnum": existing}) prop, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) # None / null is removed from enum, and property is now nullable assert prop == enum_property_factory( name=name, required=required, nullable=True, values={"A": "A", "B": "B", "C": "C"}, class_info=Class(name="ParentAnEnum", module_name="parent_an_enum"), value_type=str, default="ParentAnEnum.B", ) assert prop.nullable is True assert schemas != new_schemas, "Provided Schemas was mutated" assert new_schemas.classes_by_name == { "AnEnum": existing, "ParentAnEnum": prop, }
def test_property_from_data_ref_enum(self): from openapi_python_client.parser.properties import EnumProperty, Reference, Schemas, property_from_data name = "some_enum" data = oai.Reference.construct(ref="MyEnum") existing_enum = EnumProperty( name="an_enum", required=True, nullable=False, default=None, values={"A": "a"}, value_type=str, reference=Reference(class_name="MyEnum", module_name="my_enum"), ) schemas = Schemas(enums={"MyEnum": existing_enum}) prop, new_schemas = property_from_data(name=name, required=False, data=data, schemas=schemas, parent_name="") assert prop == EnumProperty( name="some_enum", required=False, nullable=False, default=None, values={"A": "a"}, value_type=str, reference=Reference(class_name="MyEnum", module_name="my_enum"), ) assert schemas == new_schemas
def test_build_schemas(mocker): build_model_property = mocker.patch(f"{MODULE_NAME}.build_model_property") in_data = {"1": mocker.MagicMock(enum=None), "2": mocker.MagicMock(enum=None), "3": mocker.MagicMock(enum=None)} model_1 = mocker.MagicMock() schemas_1 = mocker.MagicMock() model_2 = mocker.MagicMock() schemas_2 = mocker.MagicMock(errors=[]) error = PropertyError() schemas_3 = mocker.MagicMock() # This loops through one for each, then again to retry the error build_model_property.side_effect = [ (model_1, schemas_1), (model_2, schemas_2), (error, schemas_3), (error, schemas_3), ] from openapi_python_client.parser.properties import Schemas, build_schemas result = build_schemas(components=in_data) build_model_property.assert_has_calls( [ mocker.call(data=in_data["1"], name="1", schemas=Schemas(), required=True, parent_name=None), mocker.call(data=in_data["2"], name="2", schemas=schemas_1, required=True, parent_name=None), mocker.call(data=in_data["3"], name="3", schemas=schemas_2, required=True, parent_name=None), mocker.call(data=in_data["3"], name="3", schemas=schemas_2, required=True, parent_name=None), ] ) # schemas_3 was the last to come back from build_model_property, but it should be ignored because it's an error assert result == schemas_2 assert result.errors == [error]
def test__add_responses_error(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas schemas = Schemas() response_1_data = mocker.MagicMock() response_2_data = mocker.MagicMock() data = { "200": response_1_data, "404": response_2_data, } endpoint = self.make_endpoint() parse_error = ParseError(data=mocker.MagicMock()) response_from_data = mocker.patch(f"{MODULE_NAME}.response_from_data", return_value=(parse_error, schemas)) config = MagicMock() response, schemas = Endpoint._add_responses(endpoint=endpoint, data=data, schemas=schemas, config=config) response_from_data.assert_has_calls( [ mocker.call(status_code=200, data=response_1_data, schemas=schemas, parent_name="name", config=config), mocker.call(status_code=404, data=response_2_data, schemas=schemas, parent_name="name", config=config), ] ) assert response.errors == [ ParseError( detail=f"Cannot parse response for status code 200, response will be ommitted from generated client", data=parse_error.data, ), ParseError( detail=f"Cannot parse response for status code 404, response will be ommitted from generated client", data=parse_error.data, ), ]
def test_allof_int_enums(self, model_property_factory, enum_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct(allOf=[ oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second") ]) enum_property1 = enum_property_factory( name="an_enum", values={ "foo": 1, "bar": 2 }, value_type=int, ) enum_property2 = enum_property_factory( name="an_enum", values={"foo": 1}, value_type=int, ) schemas = Schemas( classes_by_reference={ "/First": model_property_factory(optional_properties=[enum_property1]), "/Second": model_property_factory(optional_properties=[enum_property2]), }) result = _process_properties(data=data, schemas=schemas, class_name="", config=Config()) assert result.required_props[0] == enum_property2
def test_allof_enum_incompatible_type(self, model_property_factory, enum_property_factory, int_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct(allOf=[ oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second") ]) enum_property = enum_property_factory( values={"foo": 1}, value_type=str, ) schemas = Schemas( classes_by_reference={ "/First": model_property_factory( optional_properties=[int_property_factory()]), "/Second": model_property_factory(optional_properties=[enum_property]), }) result = _process_properties(data=data, schemas=schemas, class_name="", config=Config()) assert isinstance(result, PropertyError)
def test_property_from_data_ref_model(self, model_property_factory): from openapi_python_client.parser.properties import Class, ModelProperty, Schemas, property_from_data name = "new_name" required = False class_name = "MyModel" data = oai.Reference.construct( ref=f"#/components/schemas/{class_name}") class_info = Class(name=class_name, module_name="my_model") existing_model = model_property_factory( name="old_name", class_info=class_info, ) schemas = Schemas(classes_by_reference={ f"/components/schemas/{class_name}": existing_model }) prop, new_schemas = property_from_data(name=name, required=required, data=data, schemas=schemas, parent_name="", config=Config()) assert prop == model_property_factory( name=name, required=required, class_info=class_info, ) assert schemas == new_schemas
def test_property_from_data_int_enum(self, enum_property_factory): from openapi_python_client.parser.properties import Class, Schemas, property_from_data from openapi_python_client.schema import Schema name = "my_enum" required = True nullable = False data = Schema.construct(title="anEnum", enum=[1, 2, 3], nullable=nullable, default=3) existing = enum_property_factory() schemas = Schemas(classes_by_name={"AnEnum": existing}) prop, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) assert prop == enum_property_factory( name=name, required=required, nullable=nullable, values={"VALUE_1": 1, "VALUE_2": 2, "VALUE_3": 3}, class_info=Class(name="ParentAnEnum", module_name="parent_an_enum"), value_type=int, default="ParentAnEnum.VALUE_3", ) assert schemas != new_schemas, "Provided Schemas was mutated" assert new_schemas.classes_by_name == { "AnEnum": existing, "ParentAnEnum": prop, }
def test_property_from_data_object(self, mocker): from openapi_python_client.parser.properties import Schemas, property_from_data name = mocker.MagicMock() required = mocker.MagicMock() data = oai.Schema(type="object", ) build_model_property = mocker.patch( f"{MODULE_NAME}.build_model_property") mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name) schemas = Schemas() config = MagicMock() response = property_from_data(name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config) assert response == build_model_property.return_value build_model_property.assert_called_once_with(data=data, name=name, required=required, schemas=schemas, parent_name="parent", config=config)
def test_add_body_bad_data(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas mocker.patch.object(Endpoint, "parse_request_form_body") parse_error = ParseError(data=mocker.MagicMock()) other_schemas = mocker.MagicMock() mocker.patch.object(Endpoint, "parse_request_json_body", return_value=(parse_error, other_schemas)) endpoint = self.make_endpoint() request_body = mocker.MagicMock() schemas = Schemas() result = Endpoint._add_body( endpoint=endpoint, data=oai.Operation.construct(requestBody=request_body), schemas=schemas, config=MagicMock(), ) assert result == ( ParseError(detail=f"cannot parse body of endpoint {endpoint.name}", data=parse_error.data), other_schemas, )
def test_conflicting_properties_same_types(self, model_property_factory, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct(allOf=[ oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second") ]) schemas = Schemas( classes_by_reference={ "/First": model_property_factory(optional_properties=[ string_property_factory(default="abc") ]), "/Second": model_property_factory( optional_properties=[string_property_factory()]), }) result = _process_properties(data=data, schemas=schemas, class_name="", config=Config()) assert isinstance(result, PropertyError)
def test__add_responses_status_code_error(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas schemas = Schemas() response_1_data = mocker.MagicMock() data = { "not_a_number": response_1_data, } endpoint = self.make_endpoint() parse_error = ParseError(data=mocker.MagicMock()) response_from_data = mocker.patch(f"{MODULE_NAME}.response_from_data", return_value=(parse_error, schemas)) config = MagicMock() response, schemas = Endpoint._add_responses(endpoint=endpoint, data=data, schemas=schemas, config=config) assert response.errors == [ ParseError( detail= f"Invalid response status code not_a_number (not a number), response will be ommitted from generated client" ) ] response_from_data.assert_not_called()