diff --git a/datamodel_code_generator/parser/graphql.py b/datamodel_code_generator/parser/graphql.py index baa424f39..f61644b28 100644 --- a/datamodel_code_generator/parser/graphql.py +++ b/datamodel_code_generator/parser/graphql.py @@ -37,11 +37,7 @@ escape_characters, ) from datamodel_code_generator.reference import ModelType, Reference -from datamodel_code_generator.types import ( - DataTypeManager, - StrictTypes, - Types, -) +from datamodel_code_generator.types import DataTypeManager, StrictTypes, Types try: import graphql @@ -233,6 +229,8 @@ def __init__( self.data_model_scalar_type = data_model_scalar_type self.data_model_union_type = data_model_union_type + self.use_standard_collections = use_standard_collections + self.use_union_operator = use_union_operator def _get_context_source_path_parts(self) -> Iterator[Tuple[Source, List[str]]]: # TODO (denisart): Temporarily this method duplicates @@ -283,8 +281,13 @@ def _resolve_types(self, paths: List[str], schema: graphql.GraphQLSchema) -> Non def _typename_field(self, name: str) -> DataModelFieldBase: return self.data_model_field_type( name='typename__', - data_type=DataType(literals=[name]), + data_type=DataType( + literals=[name], + use_union_operator=self.use_union_operator, + use_standard_collections=self.use_standard_collections, + ), default=name, + use_annotated=self.use_annotated, required=False, alias='__typename', use_one_literal_as_default=True, @@ -348,7 +351,11 @@ def parse_field( alias: str, field: Union[graphql.GraphQLField, graphql.GraphQLInputField], ) -> DataModelFieldBase: - final_data_type = DataType(is_optional=True) + final_data_type = DataType( + is_optional=True, + use_union_operator=self.use_union_operator, + use_standard_collections=self.use_standard_collections, + ) data_type = final_data_type obj = field.type @@ -356,7 +363,11 @@ def parse_field( if graphql.is_list_type(obj): data_type.is_list = True - new_data_type = DataType(is_optional=True) + new_data_type = DataType( + is_optional=True, + use_union_operator=self.use_union_operator, + use_standard_collections=self.use_standard_collections, + ) data_type.data_types = [new_data_type] data_type = new_data_type diff --git a/datamodel_code_generator/types.py b/datamodel_code_generator/types.py index fc7294882..0aff55b85 100644 --- a/datamodel_code_generator/types.py +++ b/datamodel_code_generator/types.py @@ -25,12 +25,7 @@ import pydantic from packaging import version -from pydantic import ( - StrictBool, - StrictInt, - StrictStr, - create_model, -) +from pydantic import StrictBool, StrictInt, StrictStr, create_model from datamodel_code_generator.format import PythonVersion from datamodel_code_generator.imports import ( diff --git a/tests/data/expected/main/csv_file_simple/output.py b/tests/data/expected/main/csv/csv_file_simple.py similarity index 100% rename from tests/data/expected/main/csv_file_simple/output.py rename to tests/data/expected/main/csv/csv_file_simple.py diff --git a/tests/data/expected/main/csv_stdin_simple/output.py b/tests/data/expected/main/csv/csv_stdin_simple.py similarity index 100% rename from tests/data/expected/main/csv_stdin_simple/output.py rename to tests/data/expected/main/csv/csv_stdin_simple.py diff --git a/tests/data/expected/main/main_graphql_additional_imports/output_isort4.py b/tests/data/expected/main/graphql/additional_imports_isort4.py similarity index 100% rename from tests/data/expected/main/main_graphql_additional_imports/output_isort4.py rename to tests/data/expected/main/graphql/additional_imports_isort4.py diff --git a/tests/data/expected/main/main_graphql_additional_imports/output_isort5.py b/tests/data/expected/main/graphql/additional_imports_isort5.py similarity index 100% rename from tests/data/expected/main/main_graphql_additional_imports/output_isort5.py rename to tests/data/expected/main/graphql/additional_imports_isort5.py diff --git a/tests/data/expected/main/graphql/annotated.py b/tests/data/expected/main/graphql/annotated.py new file mode 100644 index 000000000..eb5d53ca9 --- /dev/null +++ b/tests/data/expected/main/graphql/annotated.py @@ -0,0 +1,28 @@ +# generated by datamodel-codegen: +# filename: annotated.graphql +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import List, Optional, TypeAlias + +from pydantic import BaseModel, Field +from typing_extensions import Annotated, Literal + +Boolean: TypeAlias = bool +""" +The `Boolean` scalar type represents `true` or `false`. +""" + + +String: TypeAlias = str +""" +The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. +""" + + +class A(BaseModel): + field: String + listField: List[String] + listListField: List[List[String]] + typename__: Annotated[Optional[Literal['A']], Field(alias='__typename')] = 'A' diff --git a/tests/data/expected/main/main_graphql_custom_formatters/output.py b/tests/data/expected/main/graphql/custom_formatters.py similarity index 100% rename from tests/data/expected/main/main_graphql_custom_formatters/output.py rename to tests/data/expected/main/graphql/custom_formatters.py diff --git a/tests/data/expected/main/main_graphql_custom_scalar_types/output.py b/tests/data/expected/main/graphql/custom_scalar_types.py similarity index 100% rename from tests/data/expected/main/main_graphql_custom_scalar_types/output.py rename to tests/data/expected/main/graphql/custom_scalar_types.py diff --git a/tests/data/expected/main/main_graphql_different_types_of_fields/output.py b/tests/data/expected/main/graphql/different_types_of_fields.py similarity index 100% rename from tests/data/expected/main/main_graphql_different_types_of_fields/output.py rename to tests/data/expected/main/graphql/different_types_of_fields.py diff --git a/tests/data/expected/main/main_graphql_enums/output.py b/tests/data/expected/main/graphql/enums.py similarity index 100% rename from tests/data/expected/main/main_graphql_enums/output.py rename to tests/data/expected/main/graphql/enums.py diff --git a/tests/data/expected/main/main_graphql_field_aliases/output.py b/tests/data/expected/main/graphql/field_aliases.py similarity index 100% rename from tests/data/expected/main/main_graphql_field_aliases/output.py rename to tests/data/expected/main/graphql/field_aliases.py diff --git a/tests/data/expected/main/main_graphql_github_api/output.py b/tests/data/expected/main/graphql/github_api.py similarity index 100% rename from tests/data/expected/main/main_graphql_github_api/output.py rename to tests/data/expected/main/graphql/github_api.py diff --git a/tests/data/expected/main/main_graphql_simple_star_wars/output.py b/tests/data/expected/main/graphql/simple_star_wars.py similarity index 100% rename from tests/data/expected/main/main_graphql_simple_star_wars/output.py rename to tests/data/expected/main/graphql/simple_star_wars.py diff --git a/tests/data/expected/main/main_graphql_simple_star_wars_dataclass/output.py b/tests/data/expected/main/graphql/simple_star_wars_dataclass.py similarity index 100% rename from tests/data/expected/main/main_graphql_simple_star_wars_dataclass/output.py rename to tests/data/expected/main/graphql/simple_star_wars_dataclass.py diff --git a/tests/data/expected/main/main_graphql_union/output.py b/tests/data/expected/main/graphql/union.py similarity index 100% rename from tests/data/expected/main/main_graphql_union/output.py rename to tests/data/expected/main/graphql/union.py diff --git a/tests/data/expected/main/graphql/use_standard_collections.py b/tests/data/expected/main/graphql/use_standard_collections.py new file mode 100644 index 000000000..9cca5195a --- /dev/null +++ b/tests/data/expected/main/graphql/use_standard_collections.py @@ -0,0 +1,28 @@ +# generated by datamodel-codegen: +# filename: use-standard-collections.graphql +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import Optional, TypeAlias + +from pydantic import BaseModel, Field +from typing_extensions import Literal + +Boolean: TypeAlias = bool +""" +The `Boolean` scalar type represents `true` or `false`. +""" + + +String: TypeAlias = str +""" +The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. +""" + + +class A(BaseModel): + field: String + listField: list[String] + listListField: list[list[String]] + typename__: Optional[Literal['A']] = Field('A', alias='__typename') diff --git a/tests/data/expected/main/graphql/use_union_operator.py b/tests/data/expected/main/graphql/use_union_operator.py new file mode 100644 index 000000000..4357b3438 --- /dev/null +++ b/tests/data/expected/main/graphql/use_union_operator.py @@ -0,0 +1,41 @@ +# generated by datamodel-codegen: +# filename: use-union-operator.graphql +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import List, TypeAlias + +from pydantic import BaseModel, Field +from typing_extensions import Literal + +Boolean: TypeAlias = bool +""" +The `Boolean` scalar type represents `true` or `false`. +""" + + +String: TypeAlias = str +""" +The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. +""" + + +class A(BaseModel): + field: String + listField: List[String] + listListField: List[List[String]] + listListOptionalField: List[List[String | None]] + listOptionalField: List[String | None] + listOptionalListField: List[List[String] | None] + listOptionalListOptionalField: List[List[String | None] | None] + optionalField: String | None = None + optionalListListField: List[List[String]] | None = Field(default_factory=list) + optionalListListOptionalField: List[List[String | None]] | None = Field( + default_factory=list + ) + optionalListOptionalField: List[String | None] | None = Field(default_factory=list) + optionalListOptionalListField: List[List[String] | None] | None = Field( + default_factory=list + ) + typename__: Literal['A'] | None = Field('A', alias='__typename') diff --git a/tests/data/expected/main/main_json/output.py b/tests/data/expected/main/json/general.py similarity index 100% rename from tests/data/expected/main/main_json/output.py rename to tests/data/expected/main/json/general.py diff --git a/tests/data/expected/main/main_json_array_include_null/output.py b/tests/data/expected/main/json/json_array_include_null.py similarity index 100% rename from tests/data/expected/main/main_json_array_include_null/output.py rename to tests/data/expected/main/json/json_array_include_null.py diff --git a/tests/data/expected/main/main_json_reuse_model/output.py b/tests/data/expected/main/json/json_reuse_model.py similarity index 100% rename from tests/data/expected/main/main_json_reuse_model/output.py rename to tests/data/expected/main/json/json_reuse_model.py diff --git a/tests/data/expected/main/main_json_snake_case_field/output.py b/tests/data/expected/main/json/json_snake_case_field.py similarity index 100% rename from tests/data/expected/main/main_json_snake_case_field/output.py rename to tests/data/expected/main/json/json_snake_case_field.py diff --git a/tests/data/expected/main/simple_json_snake_case_field/output.py b/tests/data/expected/main/json/simple_json_snake_case_field.py similarity index 100% rename from tests/data/expected/main/simple_json_snake_case_field/output.py rename to tests/data/expected/main/json/simple_json_snake_case_field.py diff --git a/tests/data/expected/main/space_and_special_characters/output.py b/tests/data/expected/main/json/space_and_special_characters.py similarity index 100% rename from tests/data/expected/main/space_and_special_characters/output.py rename to tests/data/expected/main/json/space_and_special_characters.py diff --git a/tests/data/expected/main/main_typed_dict_space_and_special_characters/output.py b/tests/data/expected/main/json/typed_dict_space_and_special_characters.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_space_and_special_characters/output.py rename to tests/data/expected/main/json/typed_dict_space_and_special_characters.py diff --git a/tests/data/expected/main/main_all_of_any_of/__init__.py b/tests/data/expected/main/jsonschema/all_of_any_of/__init__.py similarity index 100% rename from tests/data/expected/main/main_all_of_any_of/__init__.py rename to tests/data/expected/main/jsonschema/all_of_any_of/__init__.py diff --git a/tests/data/expected/main/main_all_of_any_of/direct.py b/tests/data/expected/main/jsonschema/all_of_any_of/direct.py similarity index 100% rename from tests/data/expected/main/main_all_of_any_of/direct.py rename to tests/data/expected/main/jsonschema/all_of_any_of/direct.py diff --git a/tests/data/expected/main/main_all_of_any_of/reference.py b/tests/data/expected/main/jsonschema/all_of_any_of/reference.py similarity index 100% rename from tests/data/expected/main/main_all_of_any_of/reference.py rename to tests/data/expected/main/jsonschema/all_of_any_of/reference.py diff --git a/tests/data/expected/main/main_all_of_one_of/__init__.py b/tests/data/expected/main/jsonschema/all_of_one_of/__init__.py similarity index 100% rename from tests/data/expected/main/main_all_of_one_of/__init__.py rename to tests/data/expected/main/jsonschema/all_of_one_of/__init__.py diff --git a/tests/data/expected/main/main_all_of_one_of/direct.py b/tests/data/expected/main/jsonschema/all_of_one_of/direct.py similarity index 100% rename from tests/data/expected/main/main_all_of_one_of/direct.py rename to tests/data/expected/main/jsonschema/all_of_one_of/direct.py diff --git a/tests/data/expected/main/main_all_of_one_of/reference.py b/tests/data/expected/main/jsonschema/all_of_one_of/reference.py similarity index 100% rename from tests/data/expected/main/main_all_of_one_of/reference.py rename to tests/data/expected/main/jsonschema/all_of_one_of/reference.py diff --git a/tests/data/expected/main/all_of_ref/output.py b/tests/data/expected/main/jsonschema/all_of_ref.py similarity index 100% rename from tests/data/expected/main/all_of_ref/output.py rename to tests/data/expected/main/jsonschema/all_of_ref.py diff --git a/tests/data/expected/main/main_all_of_ref_self/output.py b/tests/data/expected/main/jsonschema/all_of_ref_self.py similarity index 100% rename from tests/data/expected/main/main_all_of_ref_self/output.py rename to tests/data/expected/main/jsonschema/all_of_ref_self.py diff --git a/tests/data/expected/main/main_all_of_use_default/output.py b/tests/data/expected/main/jsonschema/all_of_use_default.py similarity index 100% rename from tests/data/expected/main/main_all_of_use_default/output.py rename to tests/data/expected/main/jsonschema/all_of_use_default.py diff --git a/tests/data/expected/main/all_of_with_object/output.py b/tests/data/expected/main/jsonschema/all_of_with_object.py similarity index 100% rename from tests/data/expected/main/all_of_with_object/output.py rename to tests/data/expected/main/jsonschema/all_of_with_object.py diff --git a/tests/data/expected/main/main_array_field_constraints/output.py b/tests/data/expected/main/jsonschema/array_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_array_field_constraints/output.py rename to tests/data/expected/main/jsonschema/array_field_constraints.py diff --git a/tests/data/expected/main/main_jsonschema_array_in_additional_properties/output.py b/tests/data/expected/main/jsonschema/array_in_additional_properties.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_array_in_additional_properties/output.py rename to tests/data/expected/main/jsonschema/array_in_additional_properties.py diff --git a/tests/data/expected/main/main_autodetect/output.py b/tests/data/expected/main/jsonschema/autodetect.py similarity index 100% rename from tests/data/expected/main/main_autodetect/output.py rename to tests/data/expected/main/jsonschema/autodetect.py diff --git a/tests/data/expected/main/main_jsonschema_boolean_property/output.py b/tests/data/expected/main/jsonschema/boolean_property.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_boolean_property/output.py rename to tests/data/expected/main/jsonschema/boolean_property.py diff --git a/tests/data/expected/main/main_circular_reference/output.py b/tests/data/expected/main/jsonschema/circular_reference.py similarity index 100% rename from tests/data/expected/main/main_circular_reference/output.py rename to tests/data/expected/main/jsonschema/circular_reference.py diff --git a/tests/data/expected/main/main_jsonschema_combine_any_of_object/output.py b/tests/data/expected/main/jsonschema/combine_any_of_object.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_combine_any_of_object/output.py rename to tests/data/expected/main/jsonschema/combine_any_of_object.py diff --git a/tests/data/expected/main/main_jsonschema_combine_one_of_object/output.py b/tests/data/expected/main/jsonschema/combine_one_of_object.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_combine_one_of_object/output.py rename to tests/data/expected/main/jsonschema/combine_one_of_object.py diff --git a/tests/data/expected/main/combined_array/output.py b/tests/data/expected/main/jsonschema/combined_array.py similarity index 100% rename from tests/data/expected/main/combined_array/output.py rename to tests/data/expected/main/jsonschema/combined_array.py diff --git a/tests/data/expected/main/main_jsonschema_complex_any_of/output.py b/tests/data/expected/main/jsonschema/complex_any_of.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_complex_any_of/output.py rename to tests/data/expected/main/jsonschema/complex_any_of.py diff --git a/tests/data/expected/main/main_jsonschema_complex_one_of/output.py b/tests/data/expected/main/jsonschema/complex_one_of.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_complex_one_of/output.py rename to tests/data/expected/main/jsonschema/complex_one_of.py diff --git a/tests/data/expected/main/main_complicated_enum_default_member/output.py b/tests/data/expected/main/jsonschema/complicated_enum_default_member.py similarity index 100% rename from tests/data/expected/main/main_complicated_enum_default_member/output.py rename to tests/data/expected/main/jsonschema/complicated_enum_default_member.py diff --git a/tests/data/expected/main/main_complicated_enum_default_member_dataclass/output.py b/tests/data/expected/main/jsonschema/complicated_enum_default_member_dataclass.py similarity index 100% rename from tests/data/expected/main/main_complicated_enum_default_member_dataclass/output.py rename to tests/data/expected/main/jsonschema/complicated_enum_default_member_dataclass.py diff --git a/tests/data/expected/main/main_jsonschema_custom_base_path/output.py b/tests/data/expected/main/jsonschema/custom_base_path.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_custom_base_path/output.py rename to tests/data/expected/main/jsonschema/custom_base_path.py diff --git a/tests/data/expected/main/jsonschema/custom_formatters.py b/tests/data/expected/main/jsonschema/custom_formatters.py new file mode 100644 index 000000000..0fd54ec8a --- /dev/null +++ b/tests/data/expected/main/jsonschema/custom_formatters.py @@ -0,0 +1,23 @@ +# generated by datamodel-codegen: +# filename: person.json +# timestamp: 2019-07-26T00:00:00+00:00 + +# MIT License +# +# Copyright (c) 2023 Blah-blah +# +from __future__ import annotations + +from typing import List, Optional + +from pydantic import BaseModel, Field, conint + + +class Person(BaseModel): + firstName: Optional[str] = Field(None, description="The person's first name.") + lastName: Optional[str] = Field(None, description="The person's last name.") + age: Optional[conint(ge=0)] = Field( + None, description='Age in years which must be equal to or greater than zero.' + ) + friends: Optional[List] = None + comment: None = None diff --git a/tests/data/expected/main/main_jsonschema_custom_type_path/output.py b/tests/data/expected/main/jsonschema/custom_type_path.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_custom_type_path/output.py rename to tests/data/expected/main/jsonschema/custom_type_path.py diff --git a/tests/data/expected/main/main_jsonschema_custom_type_path_pydantic_v2/output.py b/tests/data/expected/main/jsonschema/custom_type_path_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_custom_type_path_pydantic_v2/output.py rename to tests/data/expected/main/jsonschema/custom_type_path_pydantic_v2.py diff --git a/tests/data/expected/main/main_dataclass_field/output.py b/tests/data/expected/main/jsonschema/dataclass_field.py similarity index 100% rename from tests/data/expected/main/main_dataclass_field/output.py rename to tests/data/expected/main/jsonschema/dataclass_field.py diff --git a/tests/data/expected/main/main_dataclass_field_default/output.py b/tests/data/expected/main/jsonschema/dataclass_field_default.py similarity index 100% rename from tests/data/expected/main/main_dataclass_field_default/output.py rename to tests/data/expected/main/jsonschema/dataclass_field_default.py diff --git a/tests/data/expected/main/main_jsonschema_discriminator_literals/output.py b/tests/data/expected/main/jsonschema/discriminator_literals.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_discriminator_literals/output.py rename to tests/data/expected/main/jsonschema/discriminator_literals.py diff --git a/tests/data/expected/main/discriminator_with_external_reference/output.py b/tests/data/expected/main/jsonschema/discriminator_with_external_reference.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_reference/output.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_reference.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/__init__.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/__init__.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/__init__.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/__init__.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/__init__.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/__init__.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/__init__.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/__init__.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/artificial_folder/__init__.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/artificial_folder/__init__.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/artificial_folder/__init__.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/artificial_folder/__init__.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/artificial_folder/type_1.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/artificial_folder/type_1.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/artificial_folder/type_1.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/artificial_folder/type_1.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/schema.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/schema.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/schema.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/schema.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/type_2.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/type_2.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/inner_folder/type_2.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/inner_folder/type_2.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/subfolder/__init__.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/subfolder/__init__.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/subfolder/__init__.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/subfolder/__init__.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/subfolder/type_5.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/subfolder/type_5.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/subfolder/type_5.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/subfolder/type_5.py diff --git a/tests/data/expected/main/discriminator_with_external_references_folder/type_4.py b/tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/type_4.py similarity index 100% rename from tests/data/expected/main/discriminator_with_external_references_folder/type_4.py rename to tests/data/expected/main/jsonschema/discriminator_with_external_references_folder/type_4.py diff --git a/tests/data/expected/main/duplicate_field_constraints/common.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints/common.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints/common.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints/common.py diff --git a/tests/data/expected/main/duplicate_field_constraints/test.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints/test.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints/test.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints/test.py diff --git a/tests/data/expected/main/duplicate_field_constraints_msgspec/common.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec/common.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints_msgspec/common.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec/common.py diff --git a/tests/data/expected/main/duplicate_field_constraints_msgspec/test.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec/test.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints_msgspec/test.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec/test.py diff --git a/tests/data/expected/main/duplicate_field_constraints_msgspec_py38_collapse_root_models/common.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec_py38_collapse_root_models/common.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints_msgspec_py38_collapse_root_models/common.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec_py38_collapse_root_models/common.py diff --git a/tests/data/expected/main/duplicate_field_constraints_msgspec_py38_collapse_root_models/test.py b/tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec_py38_collapse_root_models/test.py similarity index 100% rename from tests/data/expected/main/duplicate_field_constraints_msgspec_py38_collapse_root_models/test.py rename to tests/data/expected/main/jsonschema/duplicate_field_constraints_msgspec_py38_collapse_root_models/test.py diff --git a/tests/data/expected/main/main_jsonschema_duplicate_name/__init__.py b/tests/data/expected/main/jsonschema/duplicate_name/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_duplicate_name/__init__.py rename to tests/data/expected/main/jsonschema/duplicate_name/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_duplicate_name/bar.py b/tests/data/expected/main/jsonschema/duplicate_name/bar.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_duplicate_name/bar.py rename to tests/data/expected/main/jsonschema/duplicate_name/bar.py diff --git a/tests/data/expected/main/main_jsonschema_duplicate_name/foo.py b/tests/data/expected/main/jsonschema/duplicate_name/foo.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_duplicate_name/foo.py rename to tests/data/expected/main/jsonschema/duplicate_name/foo.py diff --git a/tests/data/expected/main/main_external_definitions/output.py b/tests/data/expected/main/jsonschema/external_definitions.py similarity index 100% rename from tests/data/expected/main/main_external_definitions/output.py rename to tests/data/expected/main/jsonschema/external_definitions.py diff --git a/tests/data/expected/main/main_jsonschema_external_files/output.py b/tests/data/expected/main/jsonschema/external_files.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_external_files/output.py rename to tests/data/expected/main/jsonschema/external_files.py diff --git a/tests/data/expected/main/main_external_files_in_directory/output.py b/tests/data/expected/main/jsonschema/external_files_in_directory.py similarity index 100% rename from tests/data/expected/main/main_external_files_in_directory/output.py rename to tests/data/expected/main/jsonschema/external_files_in_directory.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras/output.py b/tests/data/expected/main/jsonschema/field_extras.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras/output.py rename to tests/data/expected/main/jsonschema/field_extras.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras_field_extra_keys/output.py b/tests/data/expected/main/jsonschema/field_extras_field_extra_keys.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras_field_extra_keys/output.py rename to tests/data/expected/main/jsonschema/field_extras_field_extra_keys.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras_field_extra_keys_v2/output.py b/tests/data/expected/main/jsonschema/field_extras_field_extra_keys_v2.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras_field_extra_keys_v2/output.py rename to tests/data/expected/main/jsonschema/field_extras_field_extra_keys_v2.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras_field_include_all_keys/output.py b/tests/data/expected/main/jsonschema/field_extras_field_include_all_keys.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras_field_include_all_keys/output.py rename to tests/data/expected/main/jsonschema/field_extras_field_include_all_keys.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras_field_include_all_keys_v2/output.py b/tests/data/expected/main/jsonschema/field_extras_field_include_all_keys_v2.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras_field_include_all_keys_v2/output.py rename to tests/data/expected/main/jsonschema/field_extras_field_include_all_keys_v2.py diff --git a/tests/data/expected/main/main_jsonschema_field_extras_v2/output.py b/tests/data/expected/main/jsonschema/field_extras_v2.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_field_extras_v2/output.py rename to tests/data/expected/main/jsonschema/field_extras_v2.py diff --git a/tests/data/expected/main/main_jsonschema/output.py b/tests/data/expected/main/jsonschema/general.py similarity index 100% rename from tests/data/expected/main/main_jsonschema/output.py rename to tests/data/expected/main/jsonschema/general.py diff --git a/tests/data/expected/main/main_generate_non_pydantic_output/output.py b/tests/data/expected/main/jsonschema/generate_non_pydantic_output.py similarity index 100% rename from tests/data/expected/main/main_generate_non_pydantic_output/output.py rename to tests/data/expected/main/jsonschema/generate_non_pydantic_output.py diff --git a/tests/data/expected/main/has_default_value/output.py b/tests/data/expected/main/jsonschema/has_default_value.py similarity index 100% rename from tests/data/expected/main/has_default_value/output.py rename to tests/data/expected/main/jsonschema/has_default_value.py diff --git a/tests/data/expected/main/main_jsonschema_id/output.py b/tests/data/expected/main/jsonschema/id.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_id/output.py rename to tests/data/expected/main/jsonschema/id.py diff --git a/tests/data/expected/main/main_jsonschema_id_stdin/output.py b/tests/data/expected/main/jsonschema/id_stdin.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_id_stdin/output.py rename to tests/data/expected/main/jsonschema/id_stdin.py diff --git a/tests/data/expected/main/main_jsonschema_ids/ContactPoint.py b/tests/data/expected/main/jsonschema/ids/ContactPoint.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/ContactPoint.py rename to tests/data/expected/main/jsonschema/ids/ContactPoint.py diff --git a/tests/data/expected/main/main_jsonschema_ids/URI.py b/tests/data/expected/main/jsonschema/ids/URI.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/URI.py rename to tests/data/expected/main/jsonschema/ids/URI.py diff --git a/tests/data/expected/main/main_jsonschema_ids/__init__.py b/tests/data/expected/main/jsonschema/ids/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/__init__.py rename to tests/data/expected/main/jsonschema/ids/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_ids/id.py b/tests/data/expected/main/jsonschema/ids/id.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/id.py rename to tests/data/expected/main/jsonschema/ids/id.py diff --git a/tests/data/expected/main/main_jsonschema_ids/name.py b/tests/data/expected/main/jsonschema/ids/name.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/name.py rename to tests/data/expected/main/jsonschema/ids/name.py diff --git a/tests/data/expected/main/main_jsonschema_ids/sameAs.py b/tests/data/expected/main/jsonschema/ids/sameAs.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/sameAs.py rename to tests/data/expected/main/jsonschema/ids/sameAs.py diff --git a/tests/data/expected/main/main_jsonschema_ids/type.py b/tests/data/expected/main/jsonschema/ids/type.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_ids/type.py rename to tests/data/expected/main/jsonschema/ids/type.py diff --git a/tests/data/expected/main/main_imports_correct/__init__.py b/tests/data/expected/main/jsonschema/imports_correct/__init__.py similarity index 100% rename from tests/data/expected/main/main_imports_correct/__init__.py rename to tests/data/expected/main/jsonschema/imports_correct/__init__.py diff --git a/tests/data/expected/main/main_imports_correct/schema.py b/tests/data/expected/main/jsonschema/imports_correct/schema.py similarity index 100% rename from tests/data/expected/main/main_imports_correct/schema.py rename to tests/data/expected/main/jsonschema/imports_correct/schema.py diff --git a/tests/data/expected/main/main_imports_correct/type_1.py b/tests/data/expected/main/jsonschema/imports_correct/type_1.py similarity index 100% rename from tests/data/expected/main/main_imports_correct/type_1.py rename to tests/data/expected/main/jsonschema/imports_correct/type_1.py diff --git a/tests/data/expected/main/main_inheritance_forward_ref/output.py b/tests/data/expected/main/jsonschema/inheritance_forward_ref.py similarity index 100% rename from tests/data/expected/main/main_inheritance_forward_ref/output.py rename to tests/data/expected/main/jsonschema/inheritance_forward_ref.py diff --git a/tests/data/expected/main/main_inheritance_forward_ref_keep_model_order/output.py b/tests/data/expected/main/jsonschema/inheritance_forward_ref_keep_model_order.py similarity index 100% rename from tests/data/expected/main/main_inheritance_forward_ref_keep_model_order/output.py rename to tests/data/expected/main/jsonschema/inheritance_forward_ref_keep_model_order.py diff --git a/tests/data/expected/main/main_invalid_enum_name/output.py b/tests/data/expected/main/jsonschema/invalid_enum_name.py similarity index 100% rename from tests/data/expected/main/main_invalid_enum_name/output.py rename to tests/data/expected/main/jsonschema/invalid_enum_name.py diff --git a/tests/data/expected/main/main_invalid_enum_name_snake_case_field/output.py b/tests/data/expected/main/jsonschema/invalid_enum_name_snake_case_field.py similarity index 100% rename from tests/data/expected/main/main_invalid_enum_name_snake_case_field/output.py rename to tests/data/expected/main/jsonschema/invalid_enum_name_snake_case_field.py diff --git a/tests/data/expected/main/main_invalid_model_name/output.py b/tests/data/expected/main/jsonschema/invalid_model_name.py similarity index 100% rename from tests/data/expected/main/main_invalid_model_name/output.py rename to tests/data/expected/main/jsonschema/invalid_model_name.py diff --git a/tests/data/expected/main/main_jsonschema_items_boolean/output.py b/tests/data/expected/main/jsonschema/items_boolean.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_items_boolean/output.py rename to tests/data/expected/main/jsonschema/items_boolean.py diff --git a/tests/data/expected/main/main_json_capitalise_enum_members/output.py b/tests/data/expected/main/jsonschema/json_capitalise_enum_members.py similarity index 100% rename from tests/data/expected/main/main_json_capitalise_enum_members/output.py rename to tests/data/expected/main/jsonschema/json_capitalise_enum_members.py diff --git a/tests/data/expected/main/main_json_pointer/output.py b/tests/data/expected/main/jsonschema/json_pointer.py similarity index 100% rename from tests/data/expected/main/main_json_pointer/output.py rename to tests/data/expected/main/jsonschema/json_pointer.py diff --git a/tests/data/expected/main/main_jsonschema_json_pointer_array/output.py b/tests/data/expected/main/jsonschema/json_pointer_array.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_json_pointer_array/output.py rename to tests/data/expected/main/jsonschema/json_pointer_array.py diff --git a/tests/data/expected/main/main_json_reuse_enum/output.py b/tests/data/expected/main/jsonschema/json_reuse_enum.py similarity index 100% rename from tests/data/expected/main/main_json_reuse_enum/output.py rename to tests/data/expected/main/jsonschema/json_reuse_enum.py diff --git a/tests/data/expected/main/main_json_reuse_enum_default_member/output.py b/tests/data/expected/main/jsonschema/json_reuse_enum_default_member.py similarity index 100% rename from tests/data/expected/main/main_json_reuse_enum_default_member/output.py rename to tests/data/expected/main/jsonschema/json_reuse_enum_default_member.py diff --git a/tests/data/expected/main/main_long_description/output.py b/tests/data/expected/main/jsonschema/long_description.py similarity index 100% rename from tests/data/expected/main/main_long_description/output.py rename to tests/data/expected/main/jsonschema/long_description.py diff --git a/tests/data/expected/main/main_long_description_wrap_string_literal/output.py b/tests/data/expected/main/jsonschema/long_description_wrap_string_literal.py similarity index 100% rename from tests/data/expected/main/main_long_description_wrap_string_literal/output.py rename to tests/data/expected/main/jsonschema/long_description_wrap_string_literal.py diff --git a/tests/data/expected/main/main_root_one_of/bar.py b/tests/data/expected/main/jsonschema/main_root_one_of/bar.py similarity index 100% rename from tests/data/expected/main/main_root_one_of/bar.py rename to tests/data/expected/main/jsonschema/main_root_one_of/bar.py diff --git a/tests/data/expected/main/main_root_one_of/foo.py b/tests/data/expected/main/jsonschema/main_root_one_of/foo.py similarity index 100% rename from tests/data/expected/main/main_root_one_of/foo.py rename to tests/data/expected/main/jsonschema/main_root_one_of/foo.py diff --git a/tests/data/expected/main/main_root_one_of/union.py b/tests/data/expected/main/jsonschema/main_root_one_of/union.py similarity index 100% rename from tests/data/expected/main/main_root_one_of/union.py rename to tests/data/expected/main/jsonschema/main_root_one_of/union.py diff --git a/tests/data/expected/main/main_jsonschema_modular_default_enum_member/__init__.py b/tests/data/expected/main/jsonschema/modular_default_enum_member/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_modular_default_enum_member/__init__.py rename to tests/data/expected/main/jsonschema/modular_default_enum_member/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_modular_default_enum_member/bar.py b/tests/data/expected/main/jsonschema/modular_default_enum_member/bar.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_modular_default_enum_member/bar.py rename to tests/data/expected/main/jsonschema/modular_default_enum_member/bar.py diff --git a/tests/data/expected/main/main_jsonschema_modular_default_enum_member/foo.py b/tests/data/expected/main/jsonschema/modular_default_enum_member/foo.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_modular_default_enum_member/foo.py rename to tests/data/expected/main/jsonschema/modular_default_enum_member/foo.py diff --git a/tests/data/expected/main/multiple_files/__init__.py b/tests/data/expected/main/jsonschema/multiple_files/__init__.py similarity index 100% rename from tests/data/expected/main/multiple_files/__init__.py rename to tests/data/expected/main/jsonschema/multiple_files/__init__.py diff --git a/tests/data/expected/main/multiple_files/file_a.py b/tests/data/expected/main/jsonschema/multiple_files/file_a.py similarity index 100% rename from tests/data/expected/main/multiple_files/file_a.py rename to tests/data/expected/main/jsonschema/multiple_files/file_a.py diff --git a/tests/data/expected/main/multiple_files/file_b.py b/tests/data/expected/main/jsonschema/multiple_files/file_b.py similarity index 100% rename from tests/data/expected/main/multiple_files/file_b.py rename to tests/data/expected/main/jsonschema/multiple_files/file_b.py diff --git a/tests/data/expected/main/multiple_files/file_c.py b/tests/data/expected/main/jsonschema/multiple_files/file_c.py similarity index 100% rename from tests/data/expected/main/multiple_files/file_c.py rename to tests/data/expected/main/jsonschema/multiple_files/file_c.py diff --git a/tests/data/expected/main/multiple_files/file_d.py b/tests/data/expected/main/jsonschema/multiple_files/file_d.py similarity index 100% rename from tests/data/expected/main/multiple_files/file_d.py rename to tests/data/expected/main/jsonschema/multiple_files/file_d.py diff --git a/tests/data/expected/main/multiple_files_json_pointer/__init__.py b/tests/data/expected/main/jsonschema/multiple_files_json_pointer/__init__.py similarity index 100% rename from tests/data/expected/main/multiple_files_json_pointer/__init__.py rename to tests/data/expected/main/jsonschema/multiple_files_json_pointer/__init__.py diff --git a/tests/data/expected/main/multiple_files_json_pointer/file_a.py b/tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_a.py similarity index 100% rename from tests/data/expected/main/multiple_files_json_pointer/file_a.py rename to tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_a.py diff --git a/tests/data/expected/main/multiple_files_json_pointer/file_b.py b/tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_b.py similarity index 100% rename from tests/data/expected/main/multiple_files_json_pointer/file_b.py rename to tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_b.py diff --git a/tests/data/expected/main/multiple_files_json_pointer/file_c.py b/tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_c.py similarity index 100% rename from tests/data/expected/main/multiple_files_json_pointer/file_c.py rename to tests/data/expected/main/jsonschema/multiple_files_json_pointer/file_c.py diff --git a/tests/data/expected/main/multiple_files_self_ref/__init__.py b/tests/data/expected/main/jsonschema/multiple_files_self_ref/__init__.py similarity index 100% rename from tests/data/expected/main/multiple_files_self_ref/__init__.py rename to tests/data/expected/main/jsonschema/multiple_files_self_ref/__init__.py diff --git a/tests/data/expected/main/multiple_files_self_ref/base_test.py b/tests/data/expected/main/jsonschema/multiple_files_self_ref/base_test.py similarity index 100% rename from tests/data/expected/main/multiple_files_self_ref/base_test.py rename to tests/data/expected/main/jsonschema/multiple_files_self_ref/base_test.py diff --git a/tests/data/expected/main/multiple_files_self_ref/test.py b/tests/data/expected/main/jsonschema/multiple_files_self_ref/test.py similarity index 100% rename from tests/data/expected/main/multiple_files_self_ref/test.py rename to tests/data/expected/main/jsonschema/multiple_files_self_ref/test.py diff --git a/tests/data/expected/main/multiple_files_self_ref_single/output.py b/tests/data/expected/main/jsonschema/multiple_files_self_ref_single.py similarity index 100% rename from tests/data/expected/main/multiple_files_self_ref_single/output.py rename to tests/data/expected/main/jsonschema/multiple_files_self_ref_single.py diff --git a/tests/data/expected/main/main_nested_all_of/output.py b/tests/data/expected/main/jsonschema/nested_all_of.py similarity index 100% rename from tests/data/expected/main/main_nested_all_of/output.py rename to tests/data/expected/main/jsonschema/nested_all_of.py diff --git a/tests/data/expected/main/main_jsonschema_nested_deep/__init__.py b/tests/data/expected/main/jsonschema/nested_deep/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_deep/__init__.py rename to tests/data/expected/main/jsonschema/nested_deep/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_nested_deep/empty_parent/nested/deep.py b/tests/data/expected/main/jsonschema/nested_deep/empty_parent/nested/deep.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_deep/empty_parent/nested/deep.py rename to tests/data/expected/main/jsonschema/nested_deep/empty_parent/nested/deep.py diff --git a/tests/data/expected/main/main_jsonschema_nested_deep/nested/deep.py b/tests/data/expected/main/jsonschema/nested_deep/nested/deep.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_deep/nested/deep.py rename to tests/data/expected/main/jsonschema/nested_deep/nested/deep.py diff --git a/tests/data/expected/main/main_nested_directory/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/drink/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/drink/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/drink/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/drink/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/drink/coffee.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/drink/coffee.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/drink/coffee.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/drink/coffee.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/drink/tea.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/drink/tea.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/drink/tea.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/drink/tea.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/food.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/food.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/food.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/food.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/friends.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/friends.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/friends.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/friends.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/machine/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/machine/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/machine/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/machine/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/machine/robot.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/machine/robot.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/machine/robot.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/machine/robot.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/relative/animal/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/relative/animal/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/relative/animal/fur.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/fur.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/relative/animal/fur.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/fur.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/relative/animal/pet/__init__.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/pet/__init__.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/relative/animal/pet/__init__.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/pet/__init__.py diff --git a/tests/data/expected/main/main_nested_directory/definitions/relative/animal/pet/pet.py b/tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/pet/pet.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/definitions/relative/animal/pet/pet.py rename to tests/data/expected/main/jsonschema/nested_directory/definitions/relative/animal/pet/pet.py diff --git a/tests/data/expected/main/main_nested_directory/person.py b/tests/data/expected/main/jsonschema/nested_directory/person.py similarity index 100% rename from tests/data/expected/main/main_nested_directory/person.py rename to tests/data/expected/main/jsonschema/nested_directory/person.py diff --git a/tests/data/expected/main/main_nested_json_pointer/output.py b/tests/data/expected/main/jsonschema/nested_json_pointer.py similarity index 100% rename from tests/data/expected/main/main_nested_json_pointer/output.py rename to tests/data/expected/main/jsonschema/nested_json_pointer.py diff --git a/tests/data/expected/main/main_jsonschema_nested_skip/__init__.py b/tests/data/expected/main/jsonschema/nested_skip/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_skip/__init__.py rename to tests/data/expected/main/jsonschema/nested_skip/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_nested_skip/a/__init__.py b/tests/data/expected/main/jsonschema/nested_skip/a/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_skip/a/__init__.py rename to tests/data/expected/main/jsonschema/nested_skip/a/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_nested_skip/a/b/__init__.py b/tests/data/expected/main/jsonschema/nested_skip/a/b/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_skip/a/b/__init__.py rename to tests/data/expected/main/jsonschema/nested_skip/a/b/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_nested_skip/a/b/c/__init__.py b/tests/data/expected/main/jsonschema/nested_skip/a/b/c/__init__.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_skip/a/b/c/__init__.py rename to tests/data/expected/main/jsonschema/nested_skip/a/b/c/__init__.py diff --git a/tests/data/expected/main/main_jsonschema_nested_skip/a/b/c/d.py b/tests/data/expected/main/jsonschema/nested_skip/a/b/c/d.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nested_skip/a/b/c/d.py rename to tests/data/expected/main/jsonschema/nested_skip/a/b/c/d.py diff --git a/tests/data/expected/main/main_null/output.py b/tests/data/expected/main/jsonschema/null.py similarity index 100% rename from tests/data/expected/main/main_null/output.py rename to tests/data/expected/main/jsonschema/null.py diff --git a/tests/data/expected/main/main_null_and_array/output.py b/tests/data/expected/main/jsonschema/null_and_array.py similarity index 100% rename from tests/data/expected/main/main_null_and_array/output.py rename to tests/data/expected/main/jsonschema/null_and_array.py diff --git a/tests/data/expected/main/main_null_and_array_v2/output.py b/tests/data/expected/main/jsonschema/null_and_array_v2.py similarity index 100% rename from tests/data/expected/main/main_null_and_array_v2/output.py rename to tests/data/expected/main/jsonschema/null_and_array_v2.py diff --git a/tests/data/expected/main/main_nullable_any_of/output.py b/tests/data/expected/main/jsonschema/nullable_any_of.py similarity index 100% rename from tests/data/expected/main/main_nullable_any_of/output.py rename to tests/data/expected/main/jsonschema/nullable_any_of.py diff --git a/tests/data/expected/main/main_nullable_any_of_use_union_operator/output.py b/tests/data/expected/main/jsonschema/nullable_any_of_use_union_operator.py similarity index 100% rename from tests/data/expected/main/main_nullable_any_of_use_union_operator/output.py rename to tests/data/expected/main/jsonschema/nullable_any_of_use_union_operator.py diff --git a/tests/data/expected/main/main_jsonschema_nullable_object/output.py b/tests/data/expected/main/jsonschema/nullable_object.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_nullable_object/output.py rename to tests/data/expected/main/jsonschema/nullable_object.py diff --git a/tests/data/expected/main/main_jsonschema_object_has_one_of/output.py b/tests/data/expected/main/jsonschema/object_has_one_of.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_object_has_one_of/output.py rename to tests/data/expected/main/jsonschema/object_has_one_of.py diff --git a/tests/data/expected/main/main_one_of_with_sub_schema_array_item/output.py b/tests/data/expected/main/jsonschema/one_of_with_sub_schema_array_item.py similarity index 100% rename from tests/data/expected/main/main_one_of_with_sub_schema_array_item/output.py rename to tests/data/expected/main/jsonschema/one_of_with_sub_schema_array_item.py diff --git a/tests/data/expected/main/main_pattern/output.py b/tests/data/expected/main/jsonschema/pattern.py similarity index 100% rename from tests/data/expected/main/main_pattern/output.py rename to tests/data/expected/main/jsonschema/pattern.py diff --git a/tests/data/expected/main/main_pattern_properties/output.py b/tests/data/expected/main/jsonschema/pattern_properties.py similarity index 100% rename from tests/data/expected/main/main_pattern_properties/output.py rename to tests/data/expected/main/jsonschema/pattern_properties.py diff --git a/tests/data/expected/main/main_jsonschema_pattern_properties_by_reference/output.py b/tests/data/expected/main/jsonschema/pattern_properties_by_reference.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_pattern_properties_by_reference/output.py rename to tests/data/expected/main/jsonschema/pattern_properties_by_reference.py diff --git a/tests/data/expected/main/main_pattern_properties_field_constraints/output.py b/tests/data/expected/main/jsonschema/pattern_properties_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_pattern_properties_field_constraints/output.py rename to tests/data/expected/main/jsonschema/pattern_properties_field_constraints.py diff --git a/tests/data/expected/main/main_jsonschema_removed_parent_class/output.py b/tests/data/expected/main/jsonschema/removed_parent_class.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_removed_parent_class/output.py rename to tests/data/expected/main/jsonschema/removed_parent_class.py diff --git a/tests/data/expected/main/main_require_referenced_field/referenced.py b/tests/data/expected/main/jsonschema/require_referenced_field/referenced.py similarity index 100% rename from tests/data/expected/main/main_require_referenced_field/referenced.py rename to tests/data/expected/main/jsonschema/require_referenced_field/referenced.py diff --git a/tests/data/expected/main/main_require_referenced_field/required.py b/tests/data/expected/main/jsonschema/require_referenced_field/required.py similarity index 100% rename from tests/data/expected/main/main_require_referenced_field/required.py rename to tests/data/expected/main/jsonschema/require_referenced_field/required.py diff --git a/tests/data/expected/main/main_require_referenced_field_pydantic_v2/referenced.py b/tests/data/expected/main/jsonschema/require_referenced_field_pydantic_v2/referenced.py similarity index 100% rename from tests/data/expected/main/main_require_referenced_field_pydantic_v2/referenced.py rename to tests/data/expected/main/jsonschema/require_referenced_field_pydantic_v2/referenced.py diff --git a/tests/data/expected/main/main_require_referenced_field_pydantic_v2/required.py b/tests/data/expected/main/jsonschema/require_referenced_field_pydantic_v2/required.py similarity index 100% rename from tests/data/expected/main/main_require_referenced_field_pydantic_v2/required.py rename to tests/data/expected/main/jsonschema/require_referenced_field_pydantic_v2/required.py diff --git a/tests/data/expected/main/main_root_id/output.py b/tests/data/expected/main/jsonschema/root_id.py similarity index 100% rename from tests/data/expected/main/main_root_id/output.py rename to tests/data/expected/main/jsonschema/root_id.py diff --git a/tests/data/expected/main/main_root_id_absolute_url/output.py b/tests/data/expected/main/jsonschema/root_id_absolute_url.py similarity index 100% rename from tests/data/expected/main/main_root_id_absolute_url/output.py rename to tests/data/expected/main/jsonschema/root_id_absolute_url.py diff --git a/tests/data/expected/main/main_jsonschema_root_in_enum/output.py b/tests/data/expected/main/jsonschema/root_in_enum.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_root_in_enum/output.py rename to tests/data/expected/main/jsonschema/root_in_enum.py diff --git a/tests/data/expected/main/main_root_model_with_additional_properties/output.py b/tests/data/expected/main/jsonschema/root_model_with_additional_properties.py similarity index 100% rename from tests/data/expected/main/main_root_model_with_additional_properties/output.py rename to tests/data/expected/main/jsonschema/root_model_with_additional_properties.py diff --git a/tests/data/expected/main/main_root_model_with_additional_properties_custom_class_name/output.py b/tests/data/expected/main/jsonschema/root_model_with_additional_properties_custom_class_name.py similarity index 100% rename from tests/data/expected/main/main_root_model_with_additional_properties_custom_class_name/output.py rename to tests/data/expected/main/jsonschema/root_model_with_additional_properties_custom_class_name.py diff --git a/tests/data/expected/main/main_root_model_with_additional_properties_literal/output.py b/tests/data/expected/main/jsonschema/root_model_with_additional_properties_literal.py similarity index 100% rename from tests/data/expected/main/main_root_model_with_additional_properties_literal/output.py rename to tests/data/expected/main/jsonschema/root_model_with_additional_properties_literal.py diff --git a/tests/data/expected/main/main_root_model_with_additional_properties_use_generic_container_types/output.py b/tests/data/expected/main/jsonschema/root_model_with_additional_properties_use_generic_container_types.py similarity index 100% rename from tests/data/expected/main/main_root_model_with_additional_properties_use_generic_container_types/output.py rename to tests/data/expected/main/jsonschema/root_model_with_additional_properties_use_generic_container_types.py diff --git a/tests/data/expected/main/main_root_model_with_additional_properties_use_standard_collections/output.py b/tests/data/expected/main/jsonschema/root_model_with_additional_properties_use_standard_collections.py similarity index 100% rename from tests/data/expected/main/main_root_model_with_additional_properties_use_standard_collections/output.py rename to tests/data/expected/main/jsonschema/root_model_with_additional_properties_use_standard_collections.py diff --git a/tests/data/expected/main/main_self_reference/output.py b/tests/data/expected/main/jsonschema/self_reference.py similarity index 100% rename from tests/data/expected/main/main_self_reference/output.py rename to tests/data/expected/main/jsonschema/self_reference.py diff --git a/tests/data/expected/main/main_similar_nested_array/output.py b/tests/data/expected/main/jsonschema/similar_nested_array.py similarity index 100% rename from tests/data/expected/main/main_similar_nested_array/output.py rename to tests/data/expected/main/jsonschema/similar_nested_array.py diff --git a/tests/data/expected/main/main_space_field_enum_snake_case_field/output.py b/tests/data/expected/main/jsonschema/space_field_enum_snake_case_field.py similarity index 100% rename from tests/data/expected/main/main_space_field_enum_snake_case_field/output.py rename to tests/data/expected/main/jsonschema/space_field_enum_snake_case_field.py diff --git a/tests/data/expected/main/main_jsonschema_special_enum/output.py b/tests/data/expected/main/jsonschema/special_enum.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_enum/output.py rename to tests/data/expected/main/jsonschema/special_enum.py diff --git a/tests/data/expected/main/main_jsonschema_special_enum_empty_enum_field_name/output.py b/tests/data/expected/main/jsonschema/special_enum_empty_enum_field_name.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_enum_empty_enum_field_name/output.py rename to tests/data/expected/main/jsonschema/special_enum_empty_enum_field_name.py diff --git a/tests/data/expected/main/main_jsonschema_special_enum_special_field_name_prefix/output.py b/tests/data/expected/main/jsonschema/special_enum_special_field_name_prefix.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_enum_special_field_name_prefix/output.py rename to tests/data/expected/main/jsonschema/special_enum_special_field_name_prefix.py diff --git a/tests/data/expected/main/main_jsonschema_special_enum_special_field_name_prefix_keep_private/output.py b/tests/data/expected/main/jsonschema/special_enum_special_field_name_prefix_keep_private.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_enum_special_field_name_prefix_keep_private/output.py rename to tests/data/expected/main/jsonschema/special_enum_special_field_name_prefix_keep_private.py diff --git a/tests/data/expected/main/main_jsonschema_special_field_name/output.py b/tests/data/expected/main/jsonschema/special_field_name.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_field_name/output.py rename to tests/data/expected/main/jsonschema/special_field_name.py diff --git a/tests/data/expected/main/main_jsonschema_special_model_remove_special_field_name_prefix/output.py b/tests/data/expected/main/jsonschema/special_model_remove_special_field_name_prefix.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_special_model_remove_special_field_name_prefix/output.py rename to tests/data/expected/main/jsonschema/special_model_remove_special_field_name_prefix.py diff --git a/tests/data/expected/main/main_strict_types/output.py b/tests/data/expected/main/jsonschema/strict_types.py similarity index 100% rename from tests/data/expected/main/main_strict_types/output.py rename to tests/data/expected/main/jsonschema/strict_types.py diff --git a/tests/data/expected/main/main_strict_types_all/output.py b/tests/data/expected/main/jsonschema/strict_types_all.py similarity index 100% rename from tests/data/expected/main/main_strict_types_all/output.py rename to tests/data/expected/main/jsonschema/strict_types_all.py diff --git a/tests/data/expected/main/main_strict_types_all_field_constraints/output.py b/tests/data/expected/main/jsonschema/strict_types_all_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_strict_types_all_field_constraints/output.py rename to tests/data/expected/main/jsonschema/strict_types_all_field_constraints.py diff --git a/tests/data/expected/main/main_jsonschema_subclass_enum/output.py b/tests/data/expected/main/jsonschema/subclass_enum.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_subclass_enum/output.py rename to tests/data/expected/main/jsonschema/subclass_enum.py diff --git a/tests/data/expected/main/main_jsonschema_titles/output.py b/tests/data/expected/main/jsonschema/titles.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_titles/output.py rename to tests/data/expected/main/jsonschema/titles.py diff --git a/tests/data/expected/main/main_jsonschema_titles_use_title_as_name/output.py b/tests/data/expected/main/jsonschema/titles_use_title_as_name.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_titles_use_title_as_name/output.py rename to tests/data/expected/main/jsonschema/titles_use_title_as_name.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/__init__.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/__init__.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/__init__.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/__init__.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/__init__.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/__init__.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/api/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/__init__.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/api/__init__.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/__init__.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/__init__.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/__init__.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/__init__.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/input.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/input.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/input.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/input.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/output.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/output.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/api/path/output.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/api/path/output.py diff --git a/tests/data/expected/main/treat_dot_as_module/complex/directory/schema.py b/tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/schema.py similarity index 100% rename from tests/data/expected/main/treat_dot_as_module/complex/directory/schema.py rename to tests/data/expected/main/jsonschema/treat_dot_as_module/complex/directory/schema.py diff --git a/tests/data/expected/main/treat_dot_not_as_module/complex.directory/__init__.py b/tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/__init__.py similarity index 100% rename from tests/data/expected/main/treat_dot_not_as_module/complex.directory/__init__.py rename to tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/__init__.py diff --git a/tests/data/expected/main/treat_dot_not_as_module/complex.directory/api_path_input.py b/tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/api_path_input.py similarity index 100% rename from tests/data/expected/main/treat_dot_not_as_module/complex.directory/api_path_input.py rename to tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/api_path_input.py diff --git a/tests/data/expected/main/treat_dot_not_as_module/complex.directory/api_path_output.py b/tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/api_path_output.py similarity index 100% rename from tests/data/expected/main/treat_dot_not_as_module/complex.directory/api_path_output.py rename to tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/api_path_output.py diff --git a/tests/data/expected/main/treat_dot_not_as_module/complex.directory/schema.py b/tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/schema.py similarity index 100% rename from tests/data/expected/main/treat_dot_not_as_module/complex.directory/schema.py rename to tests/data/expected/main/jsonschema/treat_dot_not_as_module/complex.directory/schema.py diff --git a/tests/data/expected/main/main_typed_dict_not_required_nullable/output.py b/tests/data/expected/main/jsonschema/typed_dict_not_required_nullable.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_not_required_nullable/output.py rename to tests/data/expected/main/jsonschema/typed_dict_not_required_nullable.py diff --git a/tests/data/expected/main/main_typed_dict_special_field_name_with_inheritance_model/output.py b/tests/data/expected/main/jsonschema/typed_dict_special_field_name_with_inheritance_model.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_special_field_name_with_inheritance_model/output.py rename to tests/data/expected/main/jsonschema/typed_dict_special_field_name_with_inheritance_model.py diff --git a/tests/data/expected/main/use_default_with_const/output.py b/tests/data/expected/main/jsonschema/use_default_with_const.py similarity index 100% rename from tests/data/expected/main/use_default_with_const/output.py rename to tests/data/expected/main/jsonschema/use_default_with_const.py diff --git a/tests/data/expected/main/main_use_union_operator/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/drink/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/drink/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/drink/coffee.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/coffee.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/drink/coffee.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/coffee.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/drink/tea.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/tea.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/drink/tea.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/drink/tea.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/food.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/food.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/food.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/food.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/friends.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/friends.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/friends.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/friends.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/machine/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/machine/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/machine/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/machine/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/machine/robot.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/machine/robot.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/machine/robot.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/machine/robot.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/relative/animal/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/relative/animal/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/relative/animal/fur.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/fur.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/relative/animal/fur.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/fur.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/relative/animal/pet/__init__.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/pet/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/relative/animal/pet/__init__.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/pet/__init__.py diff --git a/tests/data/expected/main/main_use_union_operator/definitions/relative/animal/pet/pet.py b/tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/pet/pet.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/definitions/relative/animal/pet/pet.py rename to tests/data/expected/main/jsonschema/use_union_operator/definitions/relative/animal/pet/pet.py diff --git a/tests/data/expected/main/main_use_union_operator/person.py b/tests/data/expected/main/jsonschema/use_union_operator/person.py similarity index 100% rename from tests/data/expected/main/main_use_union_operator/person.py rename to tests/data/expected/main/jsonschema/use_union_operator/person.py diff --git a/tests/data/expected/main/main_jsonschema_without_titles_use_title_as_name/output.py b/tests/data/expected/main/jsonschema/without_titles_use_title_as_name.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_without_titles_use_title_as_name/output.py rename to tests/data/expected/main/jsonschema/without_titles_use_title_as_name.py diff --git a/tests/data/expected/main/main_all_of_with_relative_ref/output.py b/tests/data/expected/main/openapi/all_of_with_relative_ref.py similarity index 100% rename from tests/data/expected/main/main_all_of_with_relative_ref/output.py rename to tests/data/expected/main/openapi/all_of_with_relative_ref.py diff --git a/tests/data/expected/main/main_openapi_allof_required/output.py b/tests/data/expected/main/openapi/allof_required.py similarity index 100% rename from tests/data/expected/main/main_openapi_allof_required/output.py rename to tests/data/expected/main/openapi/allof_required.py diff --git a/tests/data/expected/main/allow_extra_fields/output.py b/tests/data/expected/main/openapi/allow_extra_fields.py similarity index 100% rename from tests/data/expected/main/allow_extra_fields/output.py rename to tests/data/expected/main/openapi/allow_extra_fields.py diff --git a/tests/data/expected/main/allow_extra_fields_pydantic_v2/output.py b/tests/data/expected/main/openapi/allow_extra_fields_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/allow_extra_fields_pydantic_v2/output.py rename to tests/data/expected/main/openapi/allow_extra_fields_pydantic_v2.py diff --git a/tests/data/expected/main/allow_population_by_field_name/output.py b/tests/data/expected/main/openapi/allow_population_by_field_name.py similarity index 100% rename from tests/data/expected/main/allow_population_by_field_name/output.py rename to tests/data/expected/main/openapi/allow_population_by_field_name.py diff --git a/tests/data/expected/main/allow_population_by_field_name_pydantic_v2/output.py b/tests/data/expected/main/openapi/allow_population_by_field_name_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/allow_population_by_field_name_pydantic_v2/output.py rename to tests/data/expected/main/openapi/allow_population_by_field_name_pydantic_v2.py diff --git a/tests/data/expected/main/main_base_class/output.py b/tests/data/expected/main/openapi/base_class.py similarity index 100% rename from tests/data/expected/main/main_base_class/output.py rename to tests/data/expected/main/openapi/base_class.py diff --git a/tests/data/expected/main/main_openapi_body_and_parameters/output.py b/tests/data/expected/main/openapi/body_and_parameters/general.py similarity index 100% rename from tests/data/expected/main/main_openapi_body_and_parameters/output.py rename to tests/data/expected/main/openapi/body_and_parameters/general.py diff --git a/tests/data/expected/main/main_openapi_body_and_parameters_only_paths/output.py b/tests/data/expected/main/openapi/body_and_parameters/only_paths.py similarity index 100% rename from tests/data/expected/main/main_openapi_body_and_parameters_only_paths/output.py rename to tests/data/expected/main/openapi/body_and_parameters/only_paths.py diff --git a/tests/data/expected/main/main_openapi_body_and_parameters_only_schemas/output.py b/tests/data/expected/main/openapi/body_and_parameters/only_schemas.py similarity index 100% rename from tests/data/expected/main/main_openapi_body_and_parameters_only_schemas/output.py rename to tests/data/expected/main/openapi/body_and_parameters/only_schemas.py diff --git a/tests/data/expected/main/main_openapi_body_and_parameters_remote_ref/output.py b/tests/data/expected/main/openapi/body_and_parameters/remote_ref.py similarity index 100% rename from tests/data/expected/main/main_openapi_body_and_parameters_remote_ref/output.py rename to tests/data/expected/main/openapi/body_and_parameters/remote_ref.py diff --git a/tests/data/expected/main/main_collapse_root_models/output.py b/tests/data/expected/main/openapi/collapse_root_models.py similarity index 100% rename from tests/data/expected/main/main_collapse_root_models/output.py rename to tests/data/expected/main/openapi/collapse_root_models.py diff --git a/tests/data/expected/main/main_collapse_root_models_field_constraints/output.py b/tests/data/expected/main/openapi/collapse_root_models_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_collapse_root_models_field_constraints/output.py rename to tests/data/expected/main/openapi/collapse_root_models_field_constraints.py diff --git a/tests/data/expected/main/main_collapse_root_models_with_references_to_flat_types/output.py b/tests/data/expected/main/openapi/collapse_root_models_with_references_to_flat_types.py similarity index 100% rename from tests/data/expected/main/main_collapse_root_models_with_references_to_flat_types/output.py rename to tests/data/expected/main/openapi/collapse_root_models_with_references_to_flat_types.py diff --git a/tests/data/expected/main/main_openapi_complex_reference/output.py b/tests/data/expected/main/openapi/complex_reference.py similarity index 100% rename from tests/data/expected/main/main_openapi_complex_reference/output.py rename to tests/data/expected/main/openapi/complex_reference.py diff --git a/tests/data/expected/main/main_openapi_const/output.py b/tests/data/expected/main/openapi/const.py similarity index 100% rename from tests/data/expected/main/main_openapi_const/output.py rename to tests/data/expected/main/openapi/const.py diff --git a/tests/data/expected/main/main_openapi_const_field/output.py b/tests/data/expected/main/openapi/const_field.py similarity index 100% rename from tests/data/expected/main/main_openapi_const_field/output.py rename to tests/data/expected/main/openapi/const_field.py diff --git a/tests/data/expected/main/main_openapi_const_field_msgspec/output.py b/tests/data/expected/main/openapi/const_field_msgspec.py similarity index 100% rename from tests/data/expected/main/main_openapi_const_field_msgspec/output.py rename to tests/data/expected/main/openapi/const_field_msgspec.py diff --git a/tests/data/expected/main/main_openapi_const_field_pydantic_v2/output.py b/tests/data/expected/main/openapi/const_field_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_openapi_const_field_pydantic_v2/output.py rename to tests/data/expected/main/openapi/const_field_pydantic_v2.py diff --git a/tests/data/expected/main/main_openapi_const_pydantic_v2/output.py b/tests/data/expected/main/openapi/const_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_openapi_const_pydantic_v2/output.py rename to tests/data/expected/main/openapi/const_pydantic_v2.py diff --git a/tests/data/expected/main/main_openapi_content_in_parameters/output.py b/tests/data/expected/main/openapi/content_in_parameters.py similarity index 100% rename from tests/data/expected/main/main_openapi_content_in_parameters/output.py rename to tests/data/expected/main/openapi/content_in_parameters.py diff --git a/tests/data/expected/main/main_custom_file_header/output.py b/tests/data/expected/main/openapi/custom_file_header.py similarity index 100% rename from tests/data/expected/main/main_custom_file_header/output.py rename to tests/data/expected/main/openapi/custom_file_header.py diff --git a/tests/data/expected/main/main_openapi_custom_id_pydantic_v2/output.py b/tests/data/expected/main/openapi/custom_id_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_openapi_custom_id_pydantic_v2/output.py rename to tests/data/expected/main/openapi/custom_id_pydantic_v2.py diff --git a/tests/data/expected/main/main_openapi_custom_id_pydantic_v2_custom_base/output.py b/tests/data/expected/main/openapi/custom_id_pydantic_v2_custom_base.py similarity index 100% rename from tests/data/expected/main/main_openapi_custom_id_pydantic_v2_custom_base/output.py rename to tests/data/expected/main/openapi/custom_id_pydantic_v2_custom_base.py diff --git a/tests/data/expected/main/main_custom_template_dir/output.py b/tests/data/expected/main/openapi/custom_template_dir.py similarity index 100% rename from tests/data/expected/main/main_custom_template_dir/output.py rename to tests/data/expected/main/openapi/custom_template_dir.py diff --git a/tests/data/expected/main/main_dataclass/output.py b/tests/data/expected/main/openapi/dataclass.py similarity index 100% rename from tests/data/expected/main/main_dataclass/output.py rename to tests/data/expected/main/openapi/dataclass.py diff --git a/tests/data/expected/main/main_dataclass_base_class/output.py b/tests/data/expected/main/openapi/dataclass_base_class.py similarity index 100% rename from tests/data/expected/main/main_dataclass_base_class/output.py rename to tests/data/expected/main/openapi/dataclass_base_class.py diff --git a/tests/data/expected/main/main_openapi_datetime/output.py b/tests/data/expected/main/openapi/datetime.py similarity index 100% rename from tests/data/expected/main/main_openapi_datetime/output.py rename to tests/data/expected/main/openapi/datetime.py diff --git a/tests/data/expected/main/main_openapi_datetime_pydantic_v2/output.py b/tests/data/expected/main/openapi/datetime_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_openapi_datetime_pydantic_v2/output.py rename to tests/data/expected/main/openapi/datetime_pydantic_v2.py diff --git a/tests/data/expected/main/main_openapi_default_object/Another.py b/tests/data/expected/main/openapi/default_object/Another.py similarity index 100% rename from tests/data/expected/main/main_openapi_default_object/Another.py rename to tests/data/expected/main/openapi/default_object/Another.py diff --git a/tests/data/expected/main/main_openapi_default_object/Nested.py b/tests/data/expected/main/openapi/default_object/Nested.py similarity index 100% rename from tests/data/expected/main/main_openapi_default_object/Nested.py rename to tests/data/expected/main/openapi/default_object/Nested.py diff --git a/tests/data/expected/main/main_openapi_default_object/__init__.py b/tests/data/expected/main/openapi/default_object/__init__.py similarity index 100% rename from tests/data/expected/main/main_openapi_default_object/__init__.py rename to tests/data/expected/main/openapi/default_object/__init__.py diff --git a/tests/data/expected/main/main_disable_appending_item_suffix/output.py b/tests/data/expected/main/openapi/disable_appending_item_suffix.py similarity index 100% rename from tests/data/expected/main/main_disable_appending_item_suffix/output.py rename to tests/data/expected/main/openapi/disable_appending_item_suffix.py diff --git a/tests/data/expected/main/disable_timestamp/output.py b/tests/data/expected/main/openapi/disable_timestamp.py similarity index 100% rename from tests/data/expected/main/disable_timestamp/output.py rename to tests/data/expected/main/openapi/disable_timestamp.py diff --git a/tests/data/expected/main/main_openapi_discriminator_enum/output.py b/tests/data/expected/main/openapi/discriminator/enum.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator_enum/output.py rename to tests/data/expected/main/openapi/discriminator/enum.py diff --git a/tests/data/expected/main/main_openapi_discriminator_enum_duplicate/output.py b/tests/data/expected/main/openapi/discriminator/enum_duplicate.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator_enum_duplicate/output.py rename to tests/data/expected/main/openapi/discriminator/enum_duplicate.py diff --git a/tests/data/expected/main/main_openapi_discriminator/output.py b/tests/data/expected/main/openapi/discriminator/general.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator/output.py rename to tests/data/expected/main/openapi/discriminator/general.py diff --git a/tests/data/expected/main/main_openapi_discriminator_in_array/output.py b/tests/data/expected/main/openapi/discriminator/in_array.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator_in_array/output.py rename to tests/data/expected/main/openapi/discriminator/in_array.py diff --git a/tests/data/expected/main/main_openapi_discriminator_in_array_collapse_root_models/output.py b/tests/data/expected/main/openapi/discriminator/in_array_collapse_root_models.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator_in_array_collapse_root_models/output.py rename to tests/data/expected/main/openapi/discriminator/in_array_collapse_root_models.py diff --git a/tests/data/expected/main/main_openapi_discriminator_without_mapping/output.py b/tests/data/expected/main/openapi/discriminator/without_mapping.py similarity index 100% rename from tests/data/expected/main/main_openapi_discriminator_without_mapping/output.py rename to tests/data/expected/main/openapi/discriminator/without_mapping.py diff --git a/tests/data/expected/main/enable_faux_immutability/output.py b/tests/data/expected/main/openapi/enable_faux_immutability.py similarity index 100% rename from tests/data/expected/main/enable_faux_immutability/output.py rename to tests/data/expected/main/openapi/enable_faux_immutability.py diff --git a/tests/data/expected/main/enable_faux_immutability_pydantic_v2/output.py b/tests/data/expected/main/openapi/enable_faux_immutability_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/enable_faux_immutability_pydantic_v2/output.py rename to tests/data/expected/main/openapi/enable_faux_immutability_pydantic_v2.py diff --git a/tests/data/expected/main/enable_version_header/output.py b/tests/data/expected/main/openapi/enable_version_header.py similarity index 100% rename from tests/data/expected/main/enable_version_header/output.py rename to tests/data/expected/main/openapi/enable_version_header.py diff --git a/tests/data/expected/main/main_openapi_enum_models_all/output.py b/tests/data/expected/main/openapi/enum_models/all.py similarity index 100% rename from tests/data/expected/main/main_openapi_enum_models_all/output.py rename to tests/data/expected/main/openapi/enum_models/all.py diff --git a/tests/data/expected/main/main_openapi_enum_models_as_literal_py37/output.py b/tests/data/expected/main/openapi/enum_models/as_literal_py37.py similarity index 100% rename from tests/data/expected/main/main_openapi_enum_models_as_literal_py37/output.py rename to tests/data/expected/main/openapi/enum_models/as_literal_py37.py diff --git a/tests/data/expected/main/main_openapi_enum_models_one/output.py b/tests/data/expected/main/openapi/enum_models/one.py similarity index 100% rename from tests/data/expected/main/main_openapi_enum_models_one/output.py rename to tests/data/expected/main/openapi/enum_models/one.py diff --git a/tests/data/expected/main/main_openapi_enum_models_one_literal_as_default/output.py b/tests/data/expected/main/openapi/enum_models/one_literal_as_default.py similarity index 100% rename from tests/data/expected/main/main_openapi_enum_models_one_literal_as_default/output.py rename to tests/data/expected/main/openapi/enum_models/one_literal_as_default.py diff --git a/tests/data/expected/main/external_relative_ref/module.openapi/__init__.py b/tests/data/expected/main/openapi/external_relative_ref/module.openapi/__init__.py similarity index 100% rename from tests/data/expected/main/external_relative_ref/module.openapi/__init__.py rename to tests/data/expected/main/openapi/external_relative_ref/module.openapi/__init__.py diff --git a/tests/data/expected/main/external_relative_ref/module.openapi/model_a.py b/tests/data/expected/main/openapi/external_relative_ref/module.openapi/model_a.py similarity index 100% rename from tests/data/expected/main/external_relative_ref/module.openapi/model_a.py rename to tests/data/expected/main/openapi/external_relative_ref/module.openapi/model_a.py diff --git a/tests/data/expected/main/external_relative_ref/module.openapi/modules/__init__.py b/tests/data/expected/main/openapi/external_relative_ref/module.openapi/modules/__init__.py similarity index 100% rename from tests/data/expected/main/external_relative_ref/module.openapi/modules/__init__.py rename to tests/data/expected/main/openapi/external_relative_ref/module.openapi/modules/__init__.py diff --git a/tests/data/expected/main/external_relative_ref/module.openapi/modules/quality_evaluation.py b/tests/data/expected/main/openapi/external_relative_ref/module.openapi/modules/quality_evaluation.py similarity index 100% rename from tests/data/expected/main/external_relative_ref/module.openapi/modules/quality_evaluation.py rename to tests/data/expected/main/openapi/external_relative_ref/module.openapi/modules/quality_evaluation.py diff --git a/tests/data/expected/main/main_extra_template_data_config/output.py b/tests/data/expected/main/openapi/extra_template_data_config.py similarity index 100% rename from tests/data/expected/main/main_extra_template_data_config/output.py rename to tests/data/expected/main/openapi/extra_template_data_config.py diff --git a/tests/data/expected/main/main_extra_template_data_config_pydantic_v2/output.py b/tests/data/expected/main/openapi/extra_template_data_config_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_extra_template_data_config_pydantic_v2/output.py rename to tests/data/expected/main/openapi/extra_template_data_config_pydantic_v2.py diff --git a/tests/data/expected/main/force_optional/output.py b/tests/data/expected/main/openapi/force_optional.py similarity index 100% rename from tests/data/expected/main/force_optional/output.py rename to tests/data/expected/main/openapi/force_optional.py diff --git a/tests/data/expected/main/main/output.py b/tests/data/expected/main/openapi/general.py similarity index 100% rename from tests/data/expected/main/main/output.py rename to tests/data/expected/main/openapi/general.py diff --git a/tests/data/expected/main/main_openapi_http_refs/output.py b/tests/data/expected/main/openapi/http_refs.py similarity index 100% rename from tests/data/expected/main/main_openapi_http_refs/output.py rename to tests/data/expected/main/openapi/http_refs.py diff --git a/tests/data/expected/main/main_openapi_json_pointer/output.py b/tests/data/expected/main/openapi/json_pointer.py similarity index 100% rename from tests/data/expected/main/main_openapi_json_pointer/output.py rename to tests/data/expected/main/openapi/json_pointer.py diff --git a/tests/data/expected/main/main_openapi_max_items_enum/output.py b/tests/data/expected/main/openapi/max_items_enum.py similarity index 100% rename from tests/data/expected/main/main_openapi_max_items_enum/output.py rename to tests/data/expected/main/openapi/max_items_enum.py diff --git a/tests/data/expected/main/max_min_number/output.py b/tests/data/expected/main/openapi/max_min_number.py similarity index 100% rename from tests/data/expected/main/max_min_number/output.py rename to tests/data/expected/main/openapi/max_min_number.py diff --git a/tests/data/expected/main/main_modular/__init__.py b/tests/data/expected/main/openapi/modular/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular/__init__.py rename to tests/data/expected/main/openapi/modular/__init__.py diff --git a/tests/data/expected/main/main_modular/bar.py b/tests/data/expected/main/openapi/modular/bar.py similarity index 100% rename from tests/data/expected/main/main_modular/bar.py rename to tests/data/expected/main/openapi/modular/bar.py diff --git a/tests/data/expected/main/main_modular/collections.py b/tests/data/expected/main/openapi/modular/collections.py similarity index 100% rename from tests/data/expected/main/main_modular/collections.py rename to tests/data/expected/main/openapi/modular/collections.py diff --git a/tests/data/expected/main/main_modular/foo/__init__.py b/tests/data/expected/main/openapi/modular/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular/foo/__init__.py rename to tests/data/expected/main/openapi/modular/foo/__init__.py diff --git a/tests/data/expected/main/main_modular/foo/bar.py b/tests/data/expected/main/openapi/modular/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_modular/foo/bar.py rename to tests/data/expected/main/openapi/modular/foo/bar.py diff --git a/tests/data/expected/main/main_modular/models.py b/tests/data/expected/main/openapi/modular/models.py similarity index 100% rename from tests/data/expected/main/main_modular/models.py rename to tests/data/expected/main/openapi/modular/models.py diff --git a/tests/data/expected/main/main_modular/nested/__init__.py b/tests/data/expected/main/openapi/modular/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular/nested/__init__.py rename to tests/data/expected/main/openapi/modular/nested/__init__.py diff --git a/tests/data/expected/main/main_modular/nested/foo.py b/tests/data/expected/main/openapi/modular/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_modular/nested/foo.py rename to tests/data/expected/main/openapi/modular/nested/foo.py diff --git a/tests/data/expected/main/main_modular/woo/__init__.py b/tests/data/expected/main/openapi/modular/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular/woo/__init__.py rename to tests/data/expected/main/openapi/modular/woo/__init__.py diff --git a/tests/data/expected/main/main_modular/woo/boo.py b/tests/data/expected/main/openapi/modular/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_modular/woo/boo.py rename to tests/data/expected/main/openapi/modular/woo/boo.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/__init__.py b/tests/data/expected/main/openapi/modular_custom_class_name/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/__init__.py rename to tests/data/expected/main/openapi/modular_custom_class_name/__init__.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/bar.py b/tests/data/expected/main/openapi/modular_custom_class_name/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/bar.py rename to tests/data/expected/main/openapi/modular_custom_class_name/bar.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/collections.py b/tests/data/expected/main/openapi/modular_custom_class_name/collections.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/collections.py rename to tests/data/expected/main/openapi/modular_custom_class_name/collections.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/foo/__init__.py b/tests/data/expected/main/openapi/modular_custom_class_name/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/foo/__init__.py rename to tests/data/expected/main/openapi/modular_custom_class_name/foo/__init__.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/foo/bar.py b/tests/data/expected/main/openapi/modular_custom_class_name/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/foo/bar.py rename to tests/data/expected/main/openapi/modular_custom_class_name/foo/bar.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/models.py b/tests/data/expected/main/openapi/modular_custom_class_name/models.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/models.py rename to tests/data/expected/main/openapi/modular_custom_class_name/models.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/nested/__init__.py b/tests/data/expected/main/openapi/modular_custom_class_name/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/nested/__init__.py rename to tests/data/expected/main/openapi/modular_custom_class_name/nested/__init__.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/nested/foo.py b/tests/data/expected/main/openapi/modular_custom_class_name/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/nested/foo.py rename to tests/data/expected/main/openapi/modular_custom_class_name/nested/foo.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/woo/__init__.py b/tests/data/expected/main/openapi/modular_custom_class_name/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/woo/__init__.py rename to tests/data/expected/main/openapi/modular_custom_class_name/woo/__init__.py diff --git a/tests/data/expected/main/main_modular_custom_class_name/woo/boo.py b/tests/data/expected/main/openapi/modular_custom_class_name/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_modular_custom_class_name/woo/boo.py rename to tests/data/expected/main/openapi/modular_custom_class_name/woo/boo.py diff --git a/tests/data/expected/main/main_modular_reuse_model/__init__.py b/tests/data/expected/main/openapi/modular_reuse_model/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/__init__.py rename to tests/data/expected/main/openapi/modular_reuse_model/__init__.py diff --git a/tests/data/expected/main/main_modular_reuse_model/bar.py b/tests/data/expected/main/openapi/modular_reuse_model/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/bar.py rename to tests/data/expected/main/openapi/modular_reuse_model/bar.py diff --git a/tests/data/expected/main/main_modular_reuse_model/collections.py b/tests/data/expected/main/openapi/modular_reuse_model/collections.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/collections.py rename to tests/data/expected/main/openapi/modular_reuse_model/collections.py diff --git a/tests/data/expected/main/main_modular_reuse_model/foo/__init__.py b/tests/data/expected/main/openapi/modular_reuse_model/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/foo/__init__.py rename to tests/data/expected/main/openapi/modular_reuse_model/foo/__init__.py diff --git a/tests/data/expected/main/main_modular_reuse_model/foo/bar.py b/tests/data/expected/main/openapi/modular_reuse_model/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/foo/bar.py rename to tests/data/expected/main/openapi/modular_reuse_model/foo/bar.py diff --git a/tests/data/expected/main/main_modular_reuse_model/models.py b/tests/data/expected/main/openapi/modular_reuse_model/models.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/models.py rename to tests/data/expected/main/openapi/modular_reuse_model/models.py diff --git a/tests/data/expected/main/main_modular_reuse_model/nested/__init__.py b/tests/data/expected/main/openapi/modular_reuse_model/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/nested/__init__.py rename to tests/data/expected/main/openapi/modular_reuse_model/nested/__init__.py diff --git a/tests/data/expected/main/main_modular_reuse_model/nested/foo.py b/tests/data/expected/main/openapi/modular_reuse_model/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/nested/foo.py rename to tests/data/expected/main/openapi/modular_reuse_model/nested/foo.py diff --git a/tests/data/expected/main/main_modular_reuse_model/woo/__init__.py b/tests/data/expected/main/openapi/modular_reuse_model/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/woo/__init__.py rename to tests/data/expected/main/openapi/modular_reuse_model/woo/__init__.py diff --git a/tests/data/expected/main/main_modular_reuse_model/woo/boo.py b/tests/data/expected/main/openapi/modular_reuse_model/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_modular_reuse_model/woo/boo.py rename to tests/data/expected/main/openapi/modular_reuse_model/woo/boo.py diff --git a/tests/data/expected/main/main_modular_typed_dict/__init__.py b/tests/data/expected/main/openapi/modular_typed_dict/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/__init__.py rename to tests/data/expected/main/openapi/modular_typed_dict/__init__.py diff --git a/tests/data/expected/main/main_modular_typed_dict/bar.py b/tests/data/expected/main/openapi/modular_typed_dict/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/bar.py rename to tests/data/expected/main/openapi/modular_typed_dict/bar.py diff --git a/tests/data/expected/main/main_modular_typed_dict/collections.py b/tests/data/expected/main/openapi/modular_typed_dict/collections.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/collections.py rename to tests/data/expected/main/openapi/modular_typed_dict/collections.py diff --git a/tests/data/expected/main/main_modular_typed_dict/foo/__init__.py b/tests/data/expected/main/openapi/modular_typed_dict/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/foo/__init__.py rename to tests/data/expected/main/openapi/modular_typed_dict/foo/__init__.py diff --git a/tests/data/expected/main/main_modular_typed_dict/foo/bar.py b/tests/data/expected/main/openapi/modular_typed_dict/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/foo/bar.py rename to tests/data/expected/main/openapi/modular_typed_dict/foo/bar.py diff --git a/tests/data/expected/main/main_modular_typed_dict/models.py b/tests/data/expected/main/openapi/modular_typed_dict/models.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/models.py rename to tests/data/expected/main/openapi/modular_typed_dict/models.py diff --git a/tests/data/expected/main/main_modular_typed_dict/nested/__init__.py b/tests/data/expected/main/openapi/modular_typed_dict/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/nested/__init__.py rename to tests/data/expected/main/openapi/modular_typed_dict/nested/__init__.py diff --git a/tests/data/expected/main/main_modular_typed_dict/nested/foo.py b/tests/data/expected/main/openapi/modular_typed_dict/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/nested/foo.py rename to tests/data/expected/main/openapi/modular_typed_dict/nested/foo.py diff --git a/tests/data/expected/main/main_modular_typed_dict/woo/__init__.py b/tests/data/expected/main/openapi/modular_typed_dict/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/woo/__init__.py rename to tests/data/expected/main/openapi/modular_typed_dict/woo/__init__.py diff --git a/tests/data/expected/main/main_modular_typed_dict/woo/boo.py b/tests/data/expected/main/openapi/modular_typed_dict/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_modular_typed_dict/woo/boo.py rename to tests/data/expected/main/openapi/modular_typed_dict/woo/boo.py diff --git a/tests/data/expected/main/main_openapi_msgspec_default_object/Another.py b/tests/data/expected/main/openapi/msgspec_default_object/Another.py similarity index 100% rename from tests/data/expected/main/main_openapi_msgspec_default_object/Another.py rename to tests/data/expected/main/openapi/msgspec_default_object/Another.py diff --git a/tests/data/expected/main/main_openapi_msgspec_default_object/Nested.py b/tests/data/expected/main/openapi/msgspec_default_object/Nested.py similarity index 100% rename from tests/data/expected/main/main_openapi_msgspec_default_object/Nested.py rename to tests/data/expected/main/openapi/msgspec_default_object/Nested.py diff --git a/tests/data/expected/main/main_openapi_msgspec_default_object/__init__.py b/tests/data/expected/main/openapi/msgspec_default_object/__init__.py similarity index 100% rename from tests/data/expected/main/main_openapi_msgspec_default_object/__init__.py rename to tests/data/expected/main/openapi/msgspec_default_object/__init__.py diff --git a/tests/data/expected/main/main_msgspec_struct/output.py b/tests/data/expected/main/openapi/msgspec_struct.py similarity index 100% rename from tests/data/expected/main/main_msgspec_struct/output.py rename to tests/data/expected/main/openapi/msgspec_struct.py diff --git a/tests/data/expected/main/main_msgspec_struct_snake_case/output.py b/tests/data/expected/main/openapi/msgspec_struct_snake_case.py similarity index 100% rename from tests/data/expected/main/main_msgspec_struct_snake_case/output.py rename to tests/data/expected/main/openapi/msgspec_struct_snake_case.py diff --git a/tests/data/expected/main/main_use_annotated_with_msgspec_meta_constraints/output.py b/tests/data/expected/main/openapi/msgspec_use_annotated_with_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_use_annotated_with_msgspec_meta_constraints/output.py rename to tests/data/expected/main/openapi/msgspec_use_annotated_with_field_constraints.py diff --git a/tests/data/expected/main/main_multiple_required_any_of/output.py b/tests/data/expected/main/openapi/multiple_required_any_of.py similarity index 100% rename from tests/data/expected/main/main_multiple_required_any_of/output.py rename to tests/data/expected/main/openapi/multiple_required_any_of.py diff --git a/tests/data/expected/main/main_nested_enum/output.py b/tests/data/expected/main/openapi/nested_enum.py similarity index 100% rename from tests/data/expected/main/main_nested_enum/output.py rename to tests/data/expected/main/openapi/nested_enum.py diff --git a/tests/data/expected/main/main_no_file/output.py b/tests/data/expected/main/openapi/no_file.py similarity index 100% rename from tests/data/expected/main/main_no_file/output.py rename to tests/data/expected/main/openapi/no_file.py diff --git a/tests/data/expected/main/main_openapi_nullable/output.py b/tests/data/expected/main/openapi/nullable.py similarity index 100% rename from tests/data/expected/main/main_openapi_nullable/output.py rename to tests/data/expected/main/openapi/nullable.py diff --git a/tests/data/expected/main/main_openapi_nullable_strict_nullable/output.py b/tests/data/expected/main/openapi/nullable_strict_nullable.py similarity index 100% rename from tests/data/expected/main/main_openapi_nullable_strict_nullable/output.py rename to tests/data/expected/main/openapi/nullable_strict_nullable.py diff --git a/tests/data/expected/main/main_openapi_nullable_strict_nullable_use_union_operator/output.py b/tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py similarity index 100% rename from tests/data/expected/main/main_openapi_nullable_strict_nullable_use_union_operator/output.py rename to tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py diff --git a/tests/data/expected/main/main_openapi_oas_response_reference/output.py b/tests/data/expected/main/openapi/oas_response_reference.py similarity index 100% rename from tests/data/expected/main/main_openapi_oas_response_reference/output.py rename to tests/data/expected/main/openapi/oas_response_reference.py diff --git a/tests/data/expected/main/main_openapi_override_required_all_of/output.py b/tests/data/expected/main/openapi/override_required_all_of.py similarity index 100% rename from tests/data/expected/main/main_openapi_override_required_all_of/output.py rename to tests/data/expected/main/openapi/override_required_all_of.py diff --git a/tests/data/expected/main/openapi/pattern/general.py b/tests/data/expected/main/openapi/pattern/general.py new file mode 100644 index 000000000..93da3e249 --- /dev/null +++ b/tests/data/expected/main/openapi/pattern/general.py @@ -0,0 +1,22 @@ +# generated by datamodel-codegen: +# filename: pattern.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import Optional + +from pydantic import BaseModel, constr + + +class Info(BaseModel): + hostName: Optional[ + constr( + regex=r'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9])\Z' + ) + ] = None + arn: Optional[ + constr(regex=r'(^arn:([^:]*):([^:]*):([^:]*):(|\*|[\d]{12}):(.+)$)|^\*$') + ] = None + tel: Optional[constr(regex=r'^(\([0-9]{3}\))?[0-9]{3}-[0-9]{4}$')] = None + comment: Optional[constr(regex=r'[^\b\f\n\r\t\\a+.?\'"|()]+$')] = None diff --git a/tests/data/expected/main/main_pattern_msgspec/output.py b/tests/data/expected/main/openapi/pattern/msgspec_pattern.py similarity index 100% rename from tests/data/expected/main/main_pattern_msgspec/output.py rename to tests/data/expected/main/openapi/pattern/msgspec_pattern.py diff --git a/tests/data/expected/main/main_pattern_pydantic_v2/output.py b/tests/data/expected/main/openapi/pattern/pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_pattern_pydantic_v2/output.py rename to tests/data/expected/main/openapi/pattern/pydantic_v2.py diff --git a/tests/data/expected/main/main_pattern_with_lookaround_pydantic_v2/output.py b/tests/data/expected/main/openapi/pattern_with_lookaround_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_pattern_with_lookaround_pydantic_v2/output.py rename to tests/data/expected/main/openapi/pattern_with_lookaround_pydantic_v2.py diff --git a/tests/data/expected/main/main_pattern_with_lookaround_pydantic_v2_field_constraints/output.py b/tests/data/expected/main/openapi/pattern_with_lookaround_pydantic_v2_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_pattern_with_lookaround_pydantic_v2_field_constraints/output.py rename to tests/data/expected/main/openapi/pattern_with_lookaround_pydantic_v2_field_constraints.py diff --git a/tests/data/expected/main/main_pydantic_v2/output.py b/tests/data/expected/main/openapi/pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_pydantic_v2/output.py rename to tests/data/expected/main/openapi/pydantic_v2.py diff --git a/tests/data/expected/main/main_openapi_pydantic_v2_default_object/Another.py b/tests/data/expected/main/openapi/pydantic_v2_default_object/Another.py similarity index 100% rename from tests/data/expected/main/main_openapi_pydantic_v2_default_object/Another.py rename to tests/data/expected/main/openapi/pydantic_v2_default_object/Another.py diff --git a/tests/data/expected/main/main_openapi_pydantic_v2_default_object/Nested.py b/tests/data/expected/main/openapi/pydantic_v2_default_object/Nested.py similarity index 100% rename from tests/data/expected/main/main_openapi_pydantic_v2_default_object/Nested.py rename to tests/data/expected/main/openapi/pydantic_v2_default_object/Nested.py diff --git a/tests/data/expected/main/main_openapi_pydantic_v2_default_object/__init__.py b/tests/data/expected/main/openapi/pydantic_v2_default_object/__init__.py similarity index 100% rename from tests/data/expected/main/main_openapi_pydantic_v2_default_object/__init__.py rename to tests/data/expected/main/openapi/pydantic_v2_default_object/__init__.py diff --git a/tests/data/expected/main/pyproject/output.py b/tests/data/expected/main/openapi/pyproject.py similarity index 100% rename from tests/data/expected/main/pyproject/output.py rename to tests/data/expected/main/openapi/pyproject.py diff --git a/tests/data/expected/main/pyproject_not_found/output.py b/tests/data/expected/main/openapi/pyproject_not_found.py similarity index 100% rename from tests/data/expected/main/pyproject_not_found/output.py rename to tests/data/expected/main/openapi/pyproject_not_found.py diff --git a/tests/data/expected/main/main_jsonschema_reference_same_hierarchy_directory/output.py b/tests/data/expected/main/openapi/reference_same_hierarchy_directory.py similarity index 100% rename from tests/data/expected/main/main_jsonschema_reference_same_hierarchy_directory/output.py rename to tests/data/expected/main/openapi/reference_same_hierarchy_directory.py diff --git a/tests/data/expected/main/main_openapi_reference_to_object_properties/output.py b/tests/data/expected/main/openapi/reference_to_object_properties.py similarity index 100% rename from tests/data/expected/main/main_openapi_reference_to_object_properties/output.py rename to tests/data/expected/main/openapi/reference_to_object_properties.py diff --git a/tests/data/expected/main/main_openapi_reference_to_object_properties_collapse_root_models/output.py b/tests/data/expected/main/openapi/reference_to_object_properties_collapse_root_models.py similarity index 100% rename from tests/data/expected/main/main_openapi_reference_to_object_properties_collapse_root_models/output.py rename to tests/data/expected/main/openapi/reference_to_object_properties_collapse_root_models.py diff --git a/tests/data/expected/main/main_special_yaml_keywords/output.py b/tests/data/expected/main/openapi/special_yaml_keywords.py similarity index 100% rename from tests/data/expected/main/main_special_yaml_keywords/output.py rename to tests/data/expected/main/openapi/special_yaml_keywords.py diff --git a/tests/data/expected/main/stdin/output.py b/tests/data/expected/main/openapi/stdin.py similarity index 100% rename from tests/data/expected/main/stdin/output.py rename to tests/data/expected/main/openapi/stdin.py diff --git a/tests/data/expected/main/main_subclass_enum/output.py b/tests/data/expected/main/openapi/subclass_enum.py similarity index 100% rename from tests/data/expected/main/main_subclass_enum/output.py rename to tests/data/expected/main/openapi/subclass_enum.py diff --git a/tests/data/expected/main/target_python_version/output.py b/tests/data/expected/main/openapi/target_python_version.py similarity index 100% rename from tests/data/expected/main/target_python_version/output.py rename to tests/data/expected/main/openapi/target_python_version.py diff --git a/tests/data/expected/main/main_typed_dict/output.py b/tests/data/expected/main/openapi/typed_dict.py similarity index 100% rename from tests/data/expected/main/main_typed_dict/output.py rename to tests/data/expected/main/openapi/typed_dict.py diff --git a/tests/data/expected/main/main_typed_dict_nullable/output.py b/tests/data/expected/main/openapi/typed_dict_nullable.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_nullable/output.py rename to tests/data/expected/main/openapi/typed_dict_nullable.py diff --git a/tests/data/expected/main/main_typed_dict_nullable_strict_nullable/output.py b/tests/data/expected/main/openapi/typed_dict_nullable_strict_nullable.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_nullable_strict_nullable/output.py rename to tests/data/expected/main/openapi/typed_dict_nullable_strict_nullable.py diff --git a/tests/data/expected/main/main_typed_dict_py_38/output.py b/tests/data/expected/main/openapi/typed_dict_py_38.py similarity index 100% rename from tests/data/expected/main/main_typed_dict_py_38/output.py rename to tests/data/expected/main/openapi/typed_dict_py_38.py diff --git a/tests/data/expected/main/unsorted_optional_fields/output.py b/tests/data/expected/main/openapi/unsorted_optional_fields.py similarity index 100% rename from tests/data/expected/main/unsorted_optional_fields/output.py rename to tests/data/expected/main/openapi/unsorted_optional_fields.py diff --git a/tests/data/expected/main/main_use_annotated_with_field_constraints/output.py b/tests/data/expected/main/openapi/use_annotated_with_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_use_annotated_with_field_constraints/output.py rename to tests/data/expected/main/openapi/use_annotated_with_field_constraints.py diff --git a/tests/data/expected/main/main_use_annotated_with_field_constraints_py38/output.py b/tests/data/expected/main/openapi/use_annotated_with_field_constraints_py38.py similarity index 100% rename from tests/data/expected/main/main_use_annotated_with_field_constraints_py38/output.py rename to tests/data/expected/main/openapi/use_annotated_with_field_constraints_py38.py diff --git a/tests/data/expected/main/main_use_annotated_with_field_constraints_pydantic_v2/output.py b/tests/data/expected/main/openapi/use_annotated_with_field_constraints_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_use_annotated_with_field_constraints_pydantic_v2/output.py rename to tests/data/expected/main/openapi/use_annotated_with_field_constraints_pydantic_v2.py diff --git a/tests/data/expected/main/use_default/output.py b/tests/data/expected/main/openapi/use_default.py similarity index 100% rename from tests/data/expected/main/use_default/output.py rename to tests/data/expected/main/openapi/use_default.py diff --git a/tests/data/expected/main/main_use_default_kwarg/output.py b/tests/data/expected/main/openapi/use_default_kwarg.py similarity index 100% rename from tests/data/expected/main/main_use_default_kwarg/output.py rename to tests/data/expected/main/openapi/use_default_kwarg.py diff --git a/tests/data/expected/main/main_use_generic_container_types/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types/collections.py b/tests/data/expected/main/openapi/use_generic_container_types/collections.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/collections.py rename to tests/data/expected/main/openapi/use_generic_container_types/collections.py diff --git a/tests/data/expected/main/main_use_generic_container_types/foo/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/foo/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types/foo/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types/foo/bar.py b/tests/data/expected/main/openapi/use_generic_container_types/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/foo/bar.py rename to tests/data/expected/main/openapi/use_generic_container_types/foo/bar.py diff --git a/tests/data/expected/main/main_use_generic_container_types/models.py b/tests/data/expected/main/openapi/use_generic_container_types/models.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/models.py rename to tests/data/expected/main/openapi/use_generic_container_types/models.py diff --git a/tests/data/expected/main/main_use_generic_container_types/nested/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/nested/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types/nested/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types/nested/foo.py b/tests/data/expected/main/openapi/use_generic_container_types/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/nested/foo.py rename to tests/data/expected/main/openapi/use_generic_container_types/nested/foo.py diff --git a/tests/data/expected/main/main_use_generic_container_types/woo/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/woo/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types/woo/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types/woo/boo.py b/tests/data/expected/main/openapi/use_generic_container_types/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types/woo/boo.py rename to tests/data/expected/main/openapi/use_generic_container_types/woo/boo.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/collections.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/collections.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/collections.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/collections.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/foo/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/foo/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/foo/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/foo/bar.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/foo/bar.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/foo/bar.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/models.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/models.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/models.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/models.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/nested/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/nested/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/nested/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/nested/foo.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/nested/foo.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/nested/foo.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/woo/__init__.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/woo/__init__.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/woo/__init__.py diff --git a/tests/data/expected/main/main_use_generic_container_types_standard_collections/woo/boo.py b/tests/data/expected/main/openapi/use_generic_container_types_standard_collections/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_use_generic_container_types_standard_collections/woo/boo.py rename to tests/data/expected/main/openapi/use_generic_container_types_standard_collections/woo/boo.py diff --git a/tests/data/expected/main/main_use_operation_id_as_name/output.py b/tests/data/expected/main/openapi/use_operation_id_as_name.py similarity index 100% rename from tests/data/expected/main/main_use_operation_id_as_name/output.py rename to tests/data/expected/main/openapi/use_operation_id_as_name.py diff --git a/tests/data/expected/main/main_use_standard_collections/__init__.py b/tests/data/expected/main/openapi/use_standard_collections/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/__init__.py rename to tests/data/expected/main/openapi/use_standard_collections/__init__.py diff --git a/tests/data/expected/main/main_use_standard_collections/collections.py b/tests/data/expected/main/openapi/use_standard_collections/collections.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/collections.py rename to tests/data/expected/main/openapi/use_standard_collections/collections.py diff --git a/tests/data/expected/main/main_use_standard_collections/foo/__init__.py b/tests/data/expected/main/openapi/use_standard_collections/foo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/foo/__init__.py rename to tests/data/expected/main/openapi/use_standard_collections/foo/__init__.py diff --git a/tests/data/expected/main/main_use_standard_collections/foo/bar.py b/tests/data/expected/main/openapi/use_standard_collections/foo/bar.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/foo/bar.py rename to tests/data/expected/main/openapi/use_standard_collections/foo/bar.py diff --git a/tests/data/expected/main/main_use_standard_collections/models.py b/tests/data/expected/main/openapi/use_standard_collections/models.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/models.py rename to tests/data/expected/main/openapi/use_standard_collections/models.py diff --git a/tests/data/expected/main/main_use_standard_collections/nested/__init__.py b/tests/data/expected/main/openapi/use_standard_collections/nested/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/nested/__init__.py rename to tests/data/expected/main/openapi/use_standard_collections/nested/__init__.py diff --git a/tests/data/expected/main/main_use_standard_collections/nested/foo.py b/tests/data/expected/main/openapi/use_standard_collections/nested/foo.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/nested/foo.py rename to tests/data/expected/main/openapi/use_standard_collections/nested/foo.py diff --git a/tests/data/expected/main/main_use_standard_collections/woo/__init__.py b/tests/data/expected/main/openapi/use_standard_collections/woo/__init__.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/woo/__init__.py rename to tests/data/expected/main/openapi/use_standard_collections/woo/__init__.py diff --git a/tests/data/expected/main/main_use_standard_collections/woo/boo.py b/tests/data/expected/main/openapi/use_standard_collections/woo/boo.py similarity index 100% rename from tests/data/expected/main/main_use_standard_collections/woo/boo.py rename to tests/data/expected/main/openapi/use_standard_collections/woo/boo.py diff --git a/tests/data/expected/main/validation/output.py b/tests/data/expected/main/openapi/validation.py similarity index 100% rename from tests/data/expected/main/validation/output.py rename to tests/data/expected/main/openapi/validation.py diff --git a/tests/data/expected/main/main_with_aliases/output.py b/tests/data/expected/main/openapi/with_aliases.py similarity index 100% rename from tests/data/expected/main/main_with_aliases/output.py rename to tests/data/expected/main/openapi/with_aliases.py diff --git a/tests/data/expected/main/main_with_aliases_msgspec/output.py b/tests/data/expected/main/openapi/with_aliases_msgspec.py similarity index 100% rename from tests/data/expected/main/main_with_aliases_msgspec/output.py rename to tests/data/expected/main/openapi/with_aliases_msgspec.py diff --git a/tests/data/expected/main/main_with_exclusive/output.py b/tests/data/expected/main/openapi/with_exclusive.py similarity index 100% rename from tests/data/expected/main/main_with_exclusive/output.py rename to tests/data/expected/main/openapi/with_exclusive.py diff --git a/tests/data/expected/main/main_with_field_constraints/output.py b/tests/data/expected/main/openapi/with_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints/output.py rename to tests/data/expected/main/openapi/with_field_constraints.py diff --git a/tests/data/expected/main/main_with_field_constraints_pydantic_v2/output.py b/tests/data/expected/main/openapi/with_field_constraints_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_pydantic_v2/output.py rename to tests/data/expected/main/openapi/with_field_constraints_pydantic_v2.py diff --git a/tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_generic_container_types/output.py b/tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_generic_container_types.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_generic_container_types/output.py rename to tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_generic_container_types.py diff --git a/tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_generic_container_types_set/output.py b/tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_generic_container_types_set.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_generic_container_types_set/output.py rename to tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_generic_container_types_set.py diff --git a/tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_standard_collections/output.py b/tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_standard_collections.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_standard_collections/output.py rename to tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_standard_collections.py diff --git a/tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_standard_collections_set/output.py b/tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_standard_collections_set.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_pydantic_v2_use_standard_collections_set/output.py rename to tests/data/expected/main/openapi/with_field_constraints_pydantic_v2_use_standard_collections_set.py diff --git a/tests/data/expected/main/main_with_field_constraints_use_unique_items_as_set/output.py b/tests/data/expected/main/openapi/with_field_constraints_use_unique_items_as_set.py similarity index 100% rename from tests/data/expected/main/main_with_field_constraints_use_unique_items_as_set/output.py rename to tests/data/expected/main/openapi/with_field_constraints_use_unique_items_as_set.py diff --git a/tests/data/expected/main/main_with_snake_case_field/output.py b/tests/data/expected/main/openapi/with_snake_case_field.py similarity index 100% rename from tests/data/expected/main/main_with_snake_case_field/output.py rename to tests/data/expected/main/openapi/with_snake_case_field.py diff --git a/tests/data/expected/main/main_with_strip_default_none/output.py b/tests/data/expected/main/openapi/with_strip_default_none.py similarity index 100% rename from tests/data/expected/main/main_with_strip_default_none/output.py rename to tests/data/expected/main/openapi/with_strip_default_none.py diff --git a/tests/data/expected/main/main_without_field_constraints/output.py b/tests/data/expected/main/openapi/without_field_constraints.py similarity index 100% rename from tests/data/expected/main/main_without_field_constraints/output.py rename to tests/data/expected/main/openapi/without_field_constraints.py diff --git a/tests/data/expected/main/main_without_field_constraints_pydantic_v2/output.py b/tests/data/expected/main/openapi/without_field_constraints_pydantic_v2.py similarity index 100% rename from tests/data/expected/main/main_without_field_constraints_pydantic_v2/output.py rename to tests/data/expected/main/openapi/without_field_constraints_pydantic_v2.py diff --git a/tests/data/expected/main/space_and_special_characters_dict/output.py b/tests/data/expected/main/space_and_special_characters_dict.py similarity index 100% rename from tests/data/expected/main/space_and_special_characters_dict/output.py rename to tests/data/expected/main/space_and_special_characters_dict.py diff --git a/tests/data/expected/main/main_yaml/output.py b/tests/data/expected/main/yaml.py similarity index 100% rename from tests/data/expected/main/main_yaml/output.py rename to tests/data/expected/main/yaml.py diff --git a/tests/data/graphql/additional-imports.graphql b/tests/data/graphql/additional-imports.graphql index 2c22a553e..1d9463401 100644 --- a/tests/data/graphql/additional-imports.graphql +++ b/tests/data/graphql/additional-imports.graphql @@ -9,4 +9,4 @@ type A { a: Date! b: DateTime! c: MyCustomClass! -} \ No newline at end of file +} diff --git a/tests/data/graphql/annotated.graphql b/tests/data/graphql/annotated.graphql new file mode 100644 index 000000000..dbed7de7a --- /dev/null +++ b/tests/data/graphql/annotated.graphql @@ -0,0 +1,5 @@ +type A { + field: String! + listField: [String!]! + listListField:[[String!]!]! +} diff --git a/tests/data/graphql/use-standard-collections.graphql b/tests/data/graphql/use-standard-collections.graphql new file mode 100644 index 000000000..e88dfec62 --- /dev/null +++ b/tests/data/graphql/use-standard-collections.graphql @@ -0,0 +1,6 @@ +type A { + field: String! + listField: [String!]! + listListField:[[String!]!]! +} + diff --git a/tests/data/graphql/use-union-operator.graphql b/tests/data/graphql/use-union-operator.graphql new file mode 100644 index 000000000..662ec5008 --- /dev/null +++ b/tests/data/graphql/use-union-operator.graphql @@ -0,0 +1,14 @@ +type A { + field: String! + optionalField: String + optionalListOptionalField: [String] + listOptionalField: [String]! + listField: [String!]! + optionalListListOptionalField:[[String]!] + listListOptionalField:[[String]!]! + listOptionalListOptionalField:[[String]]! + optionalListOptionalListField:[[String!]] + optionalListListField:[[String!]!] + listListField:[[String!]!]! + listOptionalListField:[[String!]]! +} diff --git a/tests/data/jsonschema/reference_same_hierarchy_directory/common/cat.yaml b/tests/data/openapi/reference_same_hierarchy_directory/common/cat.yaml similarity index 100% rename from tests/data/jsonschema/reference_same_hierarchy_directory/common/cat.yaml rename to tests/data/openapi/reference_same_hierarchy_directory/common/cat.yaml diff --git a/tests/data/jsonschema/reference_same_hierarchy_directory/public/entities.yaml b/tests/data/openapi/reference_same_hierarchy_directory/public/entities.yaml similarity index 100% rename from tests/data/jsonschema/reference_same_hierarchy_directory/public/entities.yaml rename to tests/data/openapi/reference_same_hierarchy_directory/public/entities.yaml diff --git a/tests/main/__init__.py b/tests/main/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/main/graphql/__init__.py b/tests/main/graphql/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/main/graphql/test_main_graphql.py b/tests/main/graphql/test_main_graphql.py new file mode 100644 index 000000000..2496dd7a5 --- /dev/null +++ b/tests/main/graphql/test_main_graphql.py @@ -0,0 +1,355 @@ +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory + +import black +import isort +import pytest +from freezegun import freeze_time + +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH + +GRAPHQL_DATA_PATH: Path = DATA_PATH / 'graphql' +EXPECTED_GRAPHQL_PATH: Path = EXPECTED_MAIN_PATH / 'graphql' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: pytest.MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'simple_star_wars.py', + ), + ( + 'dataclasses.dataclass', + 'simple_star_wars_dataclass.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_simple_star_wars(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'simple-star-wars.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_different_types_of_fields(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'different-types-of-fields.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'different_types_of_fields.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_custom_scalar_types(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'custom-scalar-types.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--extra-template-data', + str(GRAPHQL_DATA_PATH / 'custom-scalar-types.json'), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'custom_scalar_types.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_field_aliases(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'field-aliases.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--aliases', + str(GRAPHQL_DATA_PATH / 'field-aliases.json'), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'field_aliases.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_enums(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'enums.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() == (EXPECTED_GRAPHQL_PATH / 'enums.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_union(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'union.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() == (EXPECTED_GRAPHQL_PATH / 'union.py').read_text() + ) + + +@pytest.mark.skipif( + not isort.__version__.startswith('4.'), + reason='See https://github.com/PyCQA/isort/issues/1600 for example', +) +@freeze_time('2019-07-26') +def test_main_graphql_additional_imports_isort_4(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'additional-imports.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--extra-template-data', + str(GRAPHQL_DATA_PATH / 'additional-imports-types.json'), + '--additional-imports', + 'datetime.datetime,datetime.date,mymodule.myclass.MyCustomPythonClass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'additional_imports_isort4.py').read_text() + ) + + +@pytest.mark.skipif( + not isort.__version__.startswith('5.'), + reason='See https://github.com/PyCQA/isort/issues/1600 for example', +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_additional_imports_isort_5(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'additional-imports.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--extra-template-data', + str(GRAPHQL_DATA_PATH / 'additional-imports-types.json'), + '--additional-imports', + 'datetime.datetime,datetime.date,mymodule.myclass.MyCustomPythonClass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'additional_imports_isort5.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_custom_formatters(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'custom-scalar-types.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--custom-formatters', + 'tests.data.python.custom_formatters.add_comment', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'custom_formatters.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_use_standard_collections(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'use-standard-collections.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--use-standard-collections', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'use_standard_collections.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_use_union_operator(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'use-union-operator.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--use-union-operator', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'use_union_operator.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_graphql_annotated(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(GRAPHQL_DATA_PATH / 'annotated.graphql'), + '--output', + str(output_file), + '--input-file-type', + 'graphql', + '--use-annotated', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_GRAPHQL_PATH / 'annotated.py').read_text() + ) diff --git a/tests/main/jsonschema/__init__.py b/tests/main/jsonschema/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/main/jsonschema/test_main_jsonschema.py b/tests/main/jsonschema/test_main_jsonschema.py new file mode 100644 index 000000000..6409869d5 --- /dev/null +++ b/tests/main/jsonschema/test_main_jsonschema.py @@ -0,0 +1,3365 @@ +import json +import shutil +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import call + +import black +import isort +import pytest +from freezegun import freeze_time +from packaging import version + +from datamodel_code_generator import ( + DataModelType, + InputFileType, + chdir, + generate, +) +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH, TIMESTAMP + +try: + from pytest import TempdirFactory +except ImportError: + from _pytest.tmpdir import TempdirFactory + +CaptureFixture = pytest.CaptureFixture +FixtureRequest = pytest.FixtureRequest +MonkeyPatch = pytest.MonkeyPatch + +JSON_SCHEMA_DATA_PATH: Path = DATA_PATH / 'jsonschema' +EXPECTED_JSON_SCHEMA_PATH: Path = EXPECTED_MAIN_PATH / 'jsonschema' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_inheritance_forward_ref(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'inheritance_forward_ref.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'inheritance_forward_ref.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_inheritance_forward_ref_keep_model_order(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'inheritance_forward_ref.json'), + '--output', + str(output_file), + '--keep-model-order', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'inheritance_forward_ref_keep_model_order.py' + ).read_text() + ) + + +@pytest.mark.skip(reason='pytest-xdist does not support the test') +@freeze_time('2019-07-26') +def test_main_without_arguments(): + with pytest.raises(SystemExit): + main() + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_autodetect(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'auto', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'autodetect.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_autodetect_failed(): + with TemporaryDirectory() as input_dir, TemporaryDirectory() as output_dir: + input_file: Path = Path(input_dir) / 'input.yaml' + output_file: Path = Path(output_dir) / 'output.py' + + input_file.write_text(':') + + return_code: Exit = main( + [ + '--input', + str(input_file), + '--output', + str(output_file), + '--input-file-type', + 'auto', + ] + ) + assert return_code == Exit.ERROR + + +@freeze_time('2019-07-26') +def test_main_jsonschema(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'general.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_nested_deep(): + with TemporaryDirectory() as output_dir: + output_init_file: Path = Path(output_dir) / '__init__.py' + output_nested_file: Path = Path(output_dir) / 'nested/deep.py' + output_empty_parent_nested_file: Path = ( + Path(output_dir) / 'empty_parent/nested/deep.py' + ) + + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nested_person.json'), + '--output', + str(output_dir), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_init_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'nested_deep' / '__init__.py').read_text() + ) + + assert ( + output_nested_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'nested_deep' / 'nested' / 'deep.py' + ).read_text() + ) + assert ( + output_empty_parent_nested_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'nested_deep' + / 'empty_parent' + / 'nested' + / 'deep.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_nested_skip(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nested_skip.json'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + nested_skip_dir = EXPECTED_JSON_SCHEMA_PATH / 'nested_skip' + for path in nested_skip_dir.rglob('*.py'): + result = output_path.joinpath(path.relative_to(nested_skip_dir)).read_text() + assert result == path.read_text() + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_external_files(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'external_parent_root.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'external_files.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_multiple_files(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'multiple_files'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'multiple_files' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'null_and_array.py', + ), + ( + 'pydantic_v2.BaseModel', + 'null_and_array_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_null_and_array(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'null_and_array.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_use_default_pydantic_v2_with_json_schema_const(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'use_default_with_const.json'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + '--use-default', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'use_default_with_const.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output,option', + [ + ( + 'pydantic.BaseModel', + 'complicated_enum_default_member.py', + '--set-default-enum-member', + ), + ( + 'dataclasses.dataclass', + 'complicated_enum_default_member_dataclass.py', + '--set-default-enum-member', + ), + ( + 'dataclasses.dataclass', + 'complicated_enum_default_member_dataclass.py', + None, + ), + ], +) +def test_main_complicated_enum_default_member(output_model, expected_output, option): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + a + for a in [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'complicated_enum.json'), + '--output', + str(output_file), + option, + '--output-model', + output_model, + ] + if a + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_json_reuse_enum_default_member(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'duplicate_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--reuse-model', + '--set-default-enum-member', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'json_reuse_enum_default_member.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_invalid_model_name_failed(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--class-name', + 'with', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err + == "title='with' is invalid class name. You have to set `--class-name` option\n" + ) + + +@freeze_time('2019-07-26') +def test_main_invalid_model_name_converted(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err + == "title='1Xyz' is invalid class name. You have to set `--class-name` option\n" + ) + + +@freeze_time('2019-07-26') +def test_main_invalid_model_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--class-name', + 'ValidModelName', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'invalid_model_name.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_with_local_file(mocker): + root_id_response = mocker.Mock() + root_id_response.text = 'dummy' + person_response = mocker.Mock() + person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'root_id.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'root_id.py').read_text() + ) + httpx_get_mock.assert_not_called() + + +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_with_remote_file(mocker): + root_id_response = mocker.Mock() + root_id_response.text = 'dummy' + person_response = mocker.Mock() + person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + with TemporaryDirectory() as output_dir: + input_file = Path(output_dir, 'root_id.json') + shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id.json', input_file) + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(input_file), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'root_id.py').read_text() + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/person.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_self_refs_with_local_file(mocker): + person_response = mocker.Mock() + person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'root_id_self_ref.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'root_id.py' + ).read_text().replace( + 'filename: root_id.json', 'filename: root_id_self_ref.json' + ) + httpx_get_mock.assert_not_called() + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_self_refs_with_remote_file(mocker): + person_response = mocker.Mock() + person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + with TemporaryDirectory() as output_dir: + input_file = Path(output_dir, 'root_id_self_ref.json') + shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id_self_ref.json', input_file) + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(input_file), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'root_id.py' + ).read_text().replace( + 'filename: root_id.json', 'filename: root_id_self_ref.json' + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/person.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_with_absolute_remote_file(mocker): + root_id_response = mocker.Mock() + root_id_response.text = 'dummy' + person_response = mocker.Mock() + person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + with TemporaryDirectory() as output_dir: + input_file = Path(output_dir, 'root_id_absolute_url.json') + shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id_absolute_url.json', input_file) + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(input_file), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'root_id_absolute_url.py').read_text() + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/person.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@freeze_time('2019-07-26') +def test_main_root_id_jsonschema_with_absolute_local_file(mocker): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'root_id_absolute_url.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'root_id_absolute_url.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_id(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'id.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() == (EXPECTED_JSON_SCHEMA_PATH / 'id.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_id_as_stdin(monkeypatch): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + monkeypatch.setattr('sys.stdin', (JSON_SCHEMA_DATA_PATH / 'id.json').open()) + return_code: Exit = main( + [ + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'id_stdin.py').read_text() + ) + + +def test_main_jsonschema_ids(tmpdir_factory: TempdirFactory) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = JSON_SCHEMA_DATA_PATH / 'ids' / 'Organization.schema.json' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + main_jsonschema_ids_dir = EXPECTED_JSON_SCHEMA_PATH / 'ids' + for path in main_jsonschema_ids_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_jsonschema_ids_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_external_definitions(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'external_definitions_root.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'external_definitions.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_external_files_in_directory(tmpdir_factory: TempdirFactory) -> None: + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH + / 'external_files_in_directory' + / 'person.json' + ), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'external_files_in_directory.py' + ).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_nested_directory(tmpdir_factory: TempdirFactory) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + output_path = output_directory / 'model' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'external_files_in_directory'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + main_nested_directory = EXPECTED_JSON_SCHEMA_PATH / 'nested_directory' + + for path in main_nested_directory.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_nested_directory) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_circular_reference(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'circular_reference.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'circular_reference.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_invalid_enum_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'invalid_enum_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'invalid_enum_name.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_invalid_enum_name_snake_case_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'invalid_enum_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--snake-case-field', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'invalid_enum_name_snake_case_field.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_reuse_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'duplicate_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--reuse-model', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'json_reuse_enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_capitalise_enum_members(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'many_case_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--capitalise-enum-members', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'json_capitalise_enum_members.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_capitalise_enum_members_without_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--capitalise-enum-members', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'autodetect.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_similar_nested_array(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'similar_nested_array.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'similar_nested_array.py').read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'require_referenced_field', + ), + ( + 'pydantic_v2.BaseModel', + 'require_referenced_field_pydantic_v2', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_require_referenced_field(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_dir: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'require_referenced_field/'), + '--output', + str(output_dir), + '--input-file-type', + 'jsonschema', + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + + assert (output_dir / 'referenced.py').read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / expected_output / 'referenced.py' + ).read_text() + assert (output_dir / 'required.py').read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / expected_output / 'required.py' + ).read_text() + + +@freeze_time('2019-07-26') +def test_main_json_pointer(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'json_pointer.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'json_pointer.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_nested_json_pointer(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nested_json_pointer.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'nested_json_pointer.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_multiple_files_json_pointer(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'multiple_files_json_pointer'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'multiple_files_json_pointer' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_root_model_with_additional_properties(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' + ), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'root_model_with_additional_properties.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_root_model_with_additional_properties_use_generic_container_types(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' + ), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-generic-container-types', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'root_model_with_additional_properties_use_generic_container_types.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_root_model_with_additional_properties_use_standard_collections(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' + ), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-standard-collections', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'root_model_with_additional_properties_use_standard_collections.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_root_model_with_additional_properties_literal(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' + ), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--enum-field-as-literal', + 'all', + '--target-python-version', + '3.8', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'root_model_with_additional_properties_literal.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_multiple_files_ref(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'multiple_files_self_ref'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'multiple_files_self_ref' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_jsonschema_multiple_files_ref_test_json(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_SCHEMA_DATA_PATH / 'multiple_files_self_ref'): + return_code: Exit = main( + [ + '--input', + 'test.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'multiple_files_self_ref_single.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_space_field_enum_snake_case_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_SCHEMA_DATA_PATH / 'space_field_enum.json'): + return_code: Exit = main( + [ + '--input', + 'space_field_enum.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--snake-case-field', + '--original-field-name-delimiter', + ' ', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'space_field_enum_snake_case_field.py' + ).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_all_of_ref(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_SCHEMA_DATA_PATH / 'all_of_ref'): + return_code: Exit = main( + [ + '--input', + 'test.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--class-name', + 'Test', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'all_of_ref.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_all_of_with_object(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_SCHEMA_DATA_PATH): + return_code: Exit = main( + [ + '--input', + 'all_of_with_object.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'all_of_with_object.py').read_text() + ) + + +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@freeze_time('2019-07-26') +def test_main_combined_array(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_SCHEMA_DATA_PATH): + return_code: Exit = main( + [ + '--input', + 'combined_array.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'combined_array.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_pattern(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'pattern.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'pattern.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_generate(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + input_ = (JSON_SCHEMA_DATA_PATH / 'person.json').relative_to(Path.cwd()) + assert not input_.is_absolute() + generate( + input_=input_, + input_file_type=InputFileType.JsonSchema, + output=output_file, + ) + + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'general.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_generate_non_pydantic_output(): + """ + See https://github.com/koxudaxi/datamodel-code-generator/issues/1452. + """ + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + input_ = (JSON_SCHEMA_DATA_PATH / 'simple_string.json').relative_to(Path.cwd()) + assert not input_.is_absolute() + generate( + input_=input_, + input_file_type=InputFileType.JsonSchema, + output=output_file, + output_model_type=DataModelType.DataclassesDataclass, + ) + + file = EXPECTED_JSON_SCHEMA_PATH / 'generate_non_pydantic_output.py' + assert output_file.read_text() == file.read_text() + + +@freeze_time('2019-07-26') +def test_main_generate_from_directory(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + input_ = (JSON_SCHEMA_DATA_PATH / 'external_files_in_directory').relative_to( + Path.cwd() + ) + assert not input_.is_absolute() + assert input_.is_dir() + generate( + input_=input_, + input_file_type=InputFileType.JsonSchema, + output=output_path, + ) + + main_nested_directory = EXPECTED_JSON_SCHEMA_PATH / 'nested_directory' + + for path in main_nested_directory.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_nested_directory) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_generate_custom_class_name_generator(): + def custom_class_name_generator(title): + return f'Custom{title}' + + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + input_ = (JSON_SCHEMA_DATA_PATH / 'person.json').relative_to(Path.cwd()) + assert not input_.is_absolute() + generate( + input_=input_, + input_file_type=InputFileType.JsonSchema, + output=output_file, + custom_class_name_generator=custom_class_name_generator, + ) + + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'general.py' + ).read_text().replace('Person', 'CustomPerson') + + +@freeze_time('2019-07-26') +def test_main_generate_custom_class_name_generator_additional_properties( + tmpdir_factory: TempdirFactory, +): + output_directory = Path(tmpdir_factory.mktemp('output')) + + output_file = output_directory / 'models.py' + + def custom_class_name_generator(name): + return f'Custom{name[0].upper() + name[1:]}' + + input_ = ( + JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' + ).relative_to(Path.cwd()) + assert not input_.is_absolute() + generate( + input_=input_, + input_file_type=InputFileType.JsonSchema, + output=output_file, + custom_class_name_generator=custom_class_name_generator, + ) + + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'root_model_with_additional_properties_custom_class_name.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_http_jsonschema(mocker): + external_directory = JSON_SCHEMA_DATA_PATH / 'external_files_in_directory' + + def get_mock_response(path: str) -> mocker.Mock: + mock = mocker.Mock() + mock.text = (external_directory / path).read_text() + return mock + + httpx_get_mock = mocker.patch( + 'httpx.get', + side_effect=[ + get_mock_response('person.json'), + get_mock_response('definitions/relative/animal/pet/pet.json'), + get_mock_response('definitions/relative/animal/fur.json'), + get_mock_response('definitions/friends.json'), + get_mock_response('definitions/food.json'), + get_mock_response('definitions/machine/robot.json'), + get_mock_response('definitions/drink/coffee.json'), + get_mock_response('definitions/drink/tea.json'), + ], + ) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--url', + 'https://example.com/external_files_in_directory/person.json', + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'external_files_in_directory.py' + ).read_text().replace( + '# filename: person.json', + '# filename: https://example.com/external_files_in_directory/person.json', + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/external_files_in_directory/person.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/relative/animal/pet/pet.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/relative/animal/fur.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/friends.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/food.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/machine/robot.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/drink/coffee.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://example.com/external_files_in_directory/definitions/drink/tea.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'headers_arguments,headers_requests,query_parameters_arguments,query_parameters_requests,http_ignore_tls', + [ + ( + ('Authorization: Basic dXNlcjpwYXNz',), + [('Authorization', 'Basic dXNlcjpwYXNz')], + ('key=value',), + [('key', 'value')], + False, + ), + ( + ('Authorization: Basic dXNlcjpwYXNz', 'X-API-key: abcefg'), + [('Authorization', 'Basic dXNlcjpwYXNz'), ('X-API-key', 'abcefg')], + ('key=value', 'newkey=newvalue'), + [('key', 'value'), ('newkey', 'newvalue')], + True, + ), + ], +) +def test_main_http_jsonschema_with_http_headers_and_http_query_parameters_and_ignore_tls( + mocker, + headers_arguments, + headers_requests, + query_parameters_arguments, + query_parameters_requests, + http_ignore_tls, +): + external_directory = JSON_SCHEMA_DATA_PATH / 'external_files_in_directory' + + def get_mock_response(path: str) -> mocker.Mock: + mock = mocker.Mock() + mock.text = (external_directory / path).read_text() + return mock + + httpx_get_mock = mocker.patch( + 'httpx.get', + side_effect=[ + get_mock_response('person.json'), + get_mock_response('definitions/relative/animal/pet/pet.json'), + get_mock_response('definitions/relative/animal/fur.json'), + get_mock_response('definitions/friends.json'), + get_mock_response('definitions/food.json'), + get_mock_response('definitions/machine/robot.json'), + get_mock_response('definitions/drink/coffee.json'), + get_mock_response('definitions/drink/tea.json'), + ], + ) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + args = [ + '--url', + 'https://example.com/external_files_in_directory/person.json', + '--http-headers', + *headers_arguments, + '--http-query-parameters', + *query_parameters_arguments, + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + if http_ignore_tls: + args.append('--http-ignore-tls') + + return_code: Exit = main(args) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'external_files_in_directory.py' + ).read_text().replace( + '# filename: person.json', + '# filename: https://example.com/external_files_in_directory/person.json', + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/external_files_in_directory/person.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/relative/animal/pet/pet.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/relative/animal/fur.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/friends.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/food.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/machine/robot.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/drink/coffee.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + call( + 'https://example.com/external_files_in_directory/definitions/drink/tea.json', + headers=headers_requests, + verify=True if not http_ignore_tls else False, + follow_redirects=True, + params=query_parameters_requests, + ), + ] + ) + + +@freeze_time('2019-07-26') +def test_main_self_reference(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'self_reference.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'self_reference.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_strict_types(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'strict_types.py').read_text() + ) + + +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@freeze_time('2019-07-26') +def test_main_strict_types_all(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--strict-types', + 'str', + 'bytes', + 'int', + 'float', + 'bool', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'strict_types_all.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_strict_types_all_with_field_constraints(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--strict-types', + 'str', + 'bytes', + 'int', + 'float', + 'bool', + '--field-constraints', + ] + ) + + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'strict_types_all_field_constraints.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_special_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'special_enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_special_enum_special_field_name_prefix(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--special-field-name-prefix', + 'special', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'special_enum_special_field_name_prefix.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_special_enum_special_field_name_prefix_keep_private(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--special-field-name-prefix', + '', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'special_enum_special_field_name_prefix_keep_private.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_special_model_remove_special_field_name_prefix(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_prefix_model.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--remove-special-field-name-prefix', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'special_model_remove_special_field_name_prefix.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_subclass_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'subclass_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-subclass-enum', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'subclass_enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_special_enum_empty_enum_field_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--empty-enum-field-name', + 'empty', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'special_enum_empty_enum_field_name.py' + ).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_special_field_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'special_field_name.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'special_field_name.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_complex_one_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'complex_one_of.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'complex_one_of.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_complex_any_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'complex_any_of.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'complex_any_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_combine_one_of_object(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'combine_one_of_object.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'combine_one_of_object.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_combine_any_of_object(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'combine_any_of_object.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'combine_any_of_object.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_jsonschema_field_include_all_keys(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--field-include-all-keys', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'general.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'field_extras_field_include_all_keys.py', + ), + ( + 'pydantic_v2.BaseModel', + 'field_extras_field_include_all_keys_v2.py', + ), + ], +) +def test_main_jsonschema_field_extras_field_include_all_keys( + output_model, expected_output +): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'extras.json'), + '--output', + str(output_file), + '--output-model', + output_model, + '--input-file-type', + 'jsonschema', + '--field-include-all-keys', + '--field-extra-keys-without-x-prefix', + 'x-repr', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'field_extras_field_extra_keys.py', + ), + ( + 'pydantic_v2.BaseModel', + 'field_extras_field_extra_keys_v2.py', + ), + ], +) +def test_main_jsonschema_field_extras_field_extra_keys(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'extras.json'), + '--output', + str(output_file), + '--output-model', + output_model, + '--input-file-type', + 'jsonschema', + '--field-extra-keys', + 'key2', + 'invalid-key-1', + '--field-extra-keys-without-x-prefix', + 'x-repr', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'field_extras.py', + ), + ( + 'pydantic_v2.BaseModel', + 'field_extras_v2.py', + ), + ], +) +def test_main_jsonschema_field_extras(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'extras.json'), + '--output', + str(output_file), + '--output-model', + output_model, + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@pytest.mark.skipif( + not isort.__version__.startswith('4.'), + reason="isort 5.x don't sort pydantic modules", +) +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'custom_type_path.py', + ), + ( + 'pydantic_v2.BaseModel', + 'custom_type_path_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_jsonschema_custom_type_path(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'custom_type_path.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_custom_base_path(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'custom_base_path.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'custom_base_path.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_long_description(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'long_description.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'long_description.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_long_description_wrap_string_literal(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'long_description.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--wrap-string-literal', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'long_description_wrap_string_literal.py' + ).read_text() + ) + + +def test_version(capsys): + with pytest.raises(SystemExit) as e: + main(['--version']) + assert e.value.code == Exit.OK + captured = capsys.readouterr() + assert captured.out == '0.0.0\n' + assert captured.err == '' + + +@freeze_time('2019-07-26') +def test_jsonschema_pattern_properties(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'pattern_properties.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'pattern_properties.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_jsonschema_pattern_properties_field_constraints(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'pattern_properties.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--field-constraints', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'pattern_properties_field_constraints.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_jsonschema_titles(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'titles.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'titles.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_jsonschema_titles_use_title_as_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'titles.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-title-as-name', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'titles_use_title_as_name.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_jsonschema_without_titles_use_title_as_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'without_titles.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-title-as-name', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'without_titles_use_title_as_name.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_has_default_value(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'has_default_value.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'has_default_value.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_boolean_property(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'boolean_property.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'boolean_property.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_modular_default_enum_member( + tmpdir_factory: TempdirFactory, +) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = JSON_SCHEMA_DATA_PATH / 'modular_default_enum_member' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--set-default-enum-member', + ] + ) + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'modular_default_enum_member' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() + assert result == path.read_text() + + +@pytest.mark.skipif( + black.__version__.split('.')[0] < '22', + reason="Installed black doesn't support Python version 3.10", +) +@freeze_time('2019-07-26') +def test_main_use_union_operator(tmpdir_factory: TempdirFactory) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + output_path = output_directory / 'model' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'external_files_in_directory'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + '--use-union-operator', + ] + ) + assert return_code == Exit.OK + main_nested_directory = EXPECTED_JSON_SCHEMA_PATH / 'use_union_operator' + + for path in main_nested_directory.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_nested_directory) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize('as_module', [True, False]) +def test_treat_dot_as_module(as_module): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + if as_module: + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'treat_dot_as_module'), + '--output', + str(output_path), + '--treat-dot-as-module', + ] + ) + else: + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'treat_dot_as_module'), + '--output', + str(output_path), + ] + ) + assert return_code == Exit.OK + path_extension = ( + 'treat_dot_as_module' if as_module else 'treat_dot_not_as_module' + ) + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / path_extension + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + if as_module: + assert str(path.relative_to(main_modular_dir)).count('.') == 1 + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_jsonschema_duplicate_name(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'duplicate_name'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'duplicate_name' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_jsonschema_items_boolean(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'items_boolean.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'items_boolean.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_array_in_additional_properites(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'array_in_additional_properties.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'array_in_additional_properties.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_nullable_object(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nullable_object.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'nullable_object.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_object_has_one_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'object_has_one_of.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'object_has_one_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_json_pointer_array(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'json_pointer_array.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'json_pointer_array.py').read_text() + ) + + +@pytest.mark.filterwarnings('error') +def test_main_disable_warnings_config(capsys: CaptureFixture): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-union-operator', + '--target-python-version', + '3.6', + '--disable-warnings', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.OK + assert captured.err == '' + + +@pytest.mark.filterwarnings('error') +def test_main_disable_warnings(capsys: CaptureFixture): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'all_of_with_object.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--disable-warnings', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.OK + assert captured.err == '' + + +@freeze_time('2019-07-26') +def test_main_jsonschema_pattern_properties_by_reference(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'pattern_properties_by_reference.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'pattern_properties_by_reference.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_dataclass_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'user.json'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'dataclass_field.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_jsonschema_enum_root_literal(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'enum_in_root' / 'enum_in_root.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--use-schema-description', + '--use-title-as-name', + '--field-constraints', + '--target-python-version', + '3.9', + '--allow-population-by-field-name', + '--strip-default-none', + '--use-default', + '--enum-field-as-literal', + 'all', + '--snake-case-field', + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'root_in_enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_nullable_any_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nullable_any_of.json'), + '--output', + str(output_file), + '--field-constraints', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'nullable_any_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_nullable_any_of_use_union_operator(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nullable_any_of.json'), + '--output', + str(output_file), + '--field-constraints', + '--use-union-operator', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'nullable_any_of_use_union_operator.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_nested_all_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'nested_all_of.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'nested_all_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_all_of_any_of(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'all_of_any_of'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + all_of_any_of_dir = EXPECTED_JSON_SCHEMA_PATH / 'all_of_any_of' + for path in all_of_any_of_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(all_of_any_of_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_all_of_one_of(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'all_of_one_of'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + all_of_any_of_dir = EXPECTED_JSON_SCHEMA_PATH / 'all_of_one_of' + for path in all_of_any_of_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(all_of_any_of_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_null(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'null.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'null.py').read_text() + ) + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_typed_dict_special_field_name_with_inheritance_model(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH + / 'special_field_name_with_inheritance_model.json' + ), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH + / 'typed_dict_special_field_name_with_inheritance_model.py' + ).read_text() + ) + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_typed_dict_not_required_nullable(): + """Test main function writing to TypedDict, with combos of Optional/NotRequired.""" + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'not_required_nullable.json'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'typed_dict_not_required_nullable.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_discriminator_literals(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'discriminator_literals.json'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'discriminator_literals.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_external_discriminator(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str( + JSON_SCHEMA_DATA_PATH + / 'discriminator_with_external_reference' + / 'inner_folder' + / 'schema.json' + ), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'discriminator_with_external_reference.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_external_discriminator_folder(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'discriminator_with_external_reference'), + '--output', + str(output_path), + ] + ) + assert return_code == Exit.OK + main_modular_dir = ( + EXPECTED_JSON_SCHEMA_PATH / 'discriminator_with_external_references_folder' + ) + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_duplicate_field_constraints(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'duplicate_field_constraints'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + '--collapse-root-models', + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'duplicate_field_constraints' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.parametrize( + 'collapse_root_models,python_version,expected_output', + [ + ( + '--collapse-root-models', + '3.8', + 'duplicate_field_constraints_msgspec_py38_collapse_root_models', + ), + ( + None, + '3.9', + 'duplicate_field_constraints_msgspec', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_duplicate_field_constraints_msgspec( + collapse_root_models, python_version, expected_output +): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + a + for a in [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'duplicate_field_constraints'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + '--output-model-type', + 'msgspec.Struct', + '--target-python-version', + python_version, + collapse_root_models, + ] + if a + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / expected_output + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_dataclass_field_defs(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'user_defs.json'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_SCHEMA_PATH / 'dataclass_field.py' + ).read_text().replace('filename: user.json', 'filename: user_defs.json') + + +@freeze_time('2019-07-26') +def test_main_dataclass_default(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'user_default.json'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'dataclass_field_default.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_all_of_ref_self(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'all_of_ref_self.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'all_of_ref_self.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_array_field_constraints(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'array_field_constraints.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--target-python-version', + '3.9', + '--field-constraints', + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'array_field_constraints.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_all_of_use_default(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'all_of_default.json'), + '--output', + str(output_file), + '--use-default', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'all_of_use_default.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_root_one_of(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'root_one_of'), + '--output', + str(output_path), + '--input-file-type', + 'jsonschema', + ] + ) + assert return_code == Exit.OK + expected_directory = EXPECTED_JSON_SCHEMA_PATH / 'root_one_of' + for path in expected_directory.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(expected_directory) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_one_of_with_sub_schema_array_item(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'one_of_with_sub_schema_array_item.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_SCHEMA_PATH / 'one_of_with_sub_schema_array_item.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_jsonschema_with_custom_formatters(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + formatter_config = { + 'license_file': str( + Path(__file__).parent.parent.parent + / 'data/python/custom_formatters/license_example.txt' + ) + } + formatter_config_path = Path(output_dir, 'formatter_config') + with formatter_config_path.open('w') as f: + json.dump(formatter_config, f) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'person.json'), + '--output', + str(output_file), + '--input-file-type', + 'jsonschema', + '--custom-formatters', + 'tests.data.python.custom_formatters.add_license', + '--custom-formatters-kwargs', + str(formatter_config_path), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_SCHEMA_PATH / 'custom_formatters.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_imports_correct(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(JSON_SCHEMA_DATA_PATH / 'imports_correct'), + '--output', + str(output_path), + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_JSON_SCHEMA_PATH / 'imports_correct' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() diff --git a/tests/main/openapi/__init__.py b/tests/main/openapi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/main/openapi/test_main_openapi.py b/tests/main/openapi/test_main_openapi.py new file mode 100644 index 000000000..0f9c444aa --- /dev/null +++ b/tests/main/openapi/test_main_openapi.py @@ -0,0 +1,2798 @@ +import platform +import shutil +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import List +from unittest.mock import call + +import black +import isort +import pydantic +import pytest +from freezegun import freeze_time +from packaging import version + +try: + from pytest import TempdirFactory +except ImportError: + from _pytest.tmpdir import TempdirFactory + +from datamodel_code_generator import ( + InputFileType, + chdir, + generate, + inferred_message, +) +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH, TIMESTAMP + +CaptureFixture = pytest.CaptureFixture +MonkeyPatch = pytest.MonkeyPatch + +OPEN_API_DATA_PATH: Path = DATA_PATH / 'openapi' +EXPECTED_OPENAPI_PATH: Path = EXPECTED_MAIN_PATH / 'openapi' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'general.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_discriminator_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'discriminator_enum.yaml'), + '--output', + str(output_file), + '--target-python-version', + '3.10', + '--output-model-type', + 'pydantic_v2.BaseModel', + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'discriminator' / 'enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_discriminator_enum_duplicate(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'discriminator_enum_duplicate.yaml'), + '--output', + str(output_file), + '--target-python-version', + '3.10', + '--output-model-type', + 'pydantic_v2.BaseModel', + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'discriminator' / 'enum_duplicate.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_pydantic_basemodel(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'general.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_base_class(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--base-class', + 'custom_module.Base', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'base_class.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_target_python_version(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--target-python-version', + '3.6', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'target_python_version.py').read_text() + ) + + +@pytest.mark.benchmark +def test_main_modular(tmpdir_factory: TempdirFactory) -> None: + """Test main function on modular file.""" + + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main(['--input', str(input_filename), '--output', str(output_path)]) + main_modular_dir = EXPECTED_OPENAPI_PATH / 'modular' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() + assert result == path.read_text() + + +def test_main_modular_reuse_model(tmpdir_factory: TempdirFactory) -> None: + """Test main function on modular file.""" + + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--reuse-model', + ] + ) + main_modular_dir = EXPECTED_OPENAPI_PATH / 'modular_reuse_model' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() + assert result == path.read_text() + + +def test_main_modular_no_file() -> None: + """Test main function on modular file with no output name.""" + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + + assert main(['--input', str(input_filename)]) == Exit.ERROR + + +def test_main_modular_filename(tmpdir_factory: TempdirFactory) -> None: + """Test main function on modular file with filename.""" + + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_filename = output_directory / 'model.py' + + assert ( + main(['--input', str(input_filename), '--output', str(output_filename)]) + == Exit.ERROR + ) + + +def test_main_openapi_no_file(capsys: CaptureFixture) -> None: + """Test main function on non-modular file with no output name.""" + + input_filename = OPEN_API_DATA_PATH / 'api.yaml' + + with freeze_time(TIMESTAMP): + main(['--input', str(input_filename)]) + + captured = capsys.readouterr() + assert captured.out == (EXPECTED_OPENAPI_PATH / 'no_file.py').read_text() + assert captured.err == inferred_message.format('openapi') + '\n' + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'extra_template_data_config.py', + ), + ( + 'pydantic_v2.BaseModel', + 'extra_template_data_config_pydantic_v2.py', + ), + ], +) +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_extra_template_data_config( + capsys: CaptureFixture, output_model, expected_output +) -> None: + """Test main function with custom config data in extra template.""" + + input_filename = OPEN_API_DATA_PATH / 'api.yaml' + extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--extra-template-data', + str(extra_template_data), + '--output-model', + output_model, + ] + ) + + captured = capsys.readouterr() + assert captured.out == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + assert captured.err == inferred_message.format('openapi') + '\n' + + +def test_main_custom_template_dir_old_style(capsys: CaptureFixture) -> None: + """Test main function with custom template directory.""" + + input_filename = OPEN_API_DATA_PATH / 'api.yaml' + custom_template_dir = DATA_PATH / 'templates_old_style' + extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--custom-template-dir', + str(custom_template_dir), + '--extra-template-data', + str(extra_template_data), + ] + ) + + captured = capsys.readouterr() + assert ( + captured.out == (EXPECTED_OPENAPI_PATH / 'custom_template_dir.py').read_text() + ) + assert captured.err == inferred_message.format('openapi') + '\n' + + +def test_main_openapi_custom_template_dir(capsys: CaptureFixture) -> None: + """Test main function with custom template directory.""" + + input_filename = OPEN_API_DATA_PATH / 'api.yaml' + custom_template_dir = DATA_PATH / 'templates' + extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--custom-template-dir', + str(custom_template_dir), + '--extra-template-data', + str(extra_template_data), + ] + ) + + captured = capsys.readouterr() + assert ( + captured.out == (EXPECTED_OPENAPI_PATH / 'custom_template_dir.py').read_text() + ) + assert captured.err == inferred_message.format('openapi') + '\n' + + +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@freeze_time('2019-07-26') +def test_pyproject(): + if platform.system() == 'Windows': + + def get_path(path): + return str(path).replace('\\', '\\\\') + + else: + + def get_path(path): + return str(path) + + with TemporaryDirectory() as output_dir: + output_dir = Path(output_dir) + + with chdir(output_dir): + output_file: Path = output_dir / 'output.py' + pyproject_toml_path = Path(DATA_PATH) / 'project' / 'pyproject.toml' + pyproject_toml = ( + pyproject_toml_path.read_text() + .replace('INPUT_PATH', get_path(OPEN_API_DATA_PATH / 'api.yaml')) + .replace('OUTPUT_PATH', get_path(output_file)) + .replace( + 'ALIASES_PATH', get_path(OPEN_API_DATA_PATH / 'empty_aliases.json') + ) + .replace( + 'EXTRA_TEMPLATE_DATA_PATH', + get_path(OPEN_API_DATA_PATH / 'empty_data.json'), + ) + .replace('CUSTOM_TEMPLATE_DIR_PATH', get_path(output_dir)) + ) + (output_dir / 'pyproject.toml').write_text(pyproject_toml) + + return_code: Exit = main([]) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'pyproject.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_pyproject_not_found(): + with TemporaryDirectory() as output_dir: + output_dir = Path(output_dir) + with chdir(output_dir): + output_file: Path = output_dir / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'pyproject_not_found.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_stdin(monkeypatch): + with TemporaryDirectory() as output_dir: + output_dir = Path(output_dir) + output_file: Path = output_dir / 'output.py' + monkeypatch.setattr('sys.stdin', (OPEN_API_DATA_PATH / 'api.yaml').open()) + return_code: Exit = main( + [ + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() == (EXPECTED_OPENAPI_PATH / 'stdin.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_validation(mocker): + mock_prance = mocker.patch('prance.BaseParser') + + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--validation', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'validation.py').read_text() + ) + mock_prance.assert_called_once() + + +@freeze_time('2019-07-26') +def test_validation_failed(mocker): + mock_prance = mocker.patch('prance.BaseParser', side_effect=Exception('error')) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + assert ( + main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'invalid.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--validation', + ] + ) + == Exit.ERROR + ) + mock_prance.assert_called_once() + + +@pytest.mark.parametrize( + 'output_model,expected_output, args', + [ + ('pydantic.BaseModel', 'with_field_constraints.py', []), + ( + 'pydantic.BaseModel', + 'with_field_constraints_use_unique_items_as_set.py', + ['--use-unique-items-as-set'], + ), + ('pydantic_v2.BaseModel', 'with_field_constraints_pydantic_v2.py', []), + ( + 'pydantic_v2.BaseModel', + 'with_field_constraints_pydantic_v2_use_generic_container_types.py', + ['--use-generic-container-types'], + ), + ( + 'pydantic_v2.BaseModel', + 'with_field_constraints_pydantic_v2_use_generic_container_types_set.py', + ['--use-generic-container-types', '--use-unique-items-as-set'], + ), + ( + 'pydantic_v2.BaseModel', + 'with_field_constraints_pydantic_v2_use_standard_collections.py', + [ + '--use-standard-collections', + ], + ), + ( + 'pydantic_v2.BaseModel', + 'with_field_constraints_pydantic_v2_use_standard_collections_set.py', + ['--use-standard-collections', '--use-unique-items-as-set'], + ), + ], +) +@freeze_time('2019-07-26') +def test_main_with_field_constraints(output_model, expected_output, args): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--field-constraints', + '--output-model-type', + output_model, + *args, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'without_field_constraints.py', + ), + ( + 'pydantic_v2.BaseModel', + 'without_field_constraints_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_without_field_constraints(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'with_aliases.py', + ), + ( + 'msgspec.Struct', + 'with_aliases_msgspec.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_with_aliases(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--aliases', + str(OPEN_API_DATA_PATH / 'aliases.json'), + '--target-python', + '3.9', + '--output-model', + output_model, + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +def test_main_with_bad_aliases(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--aliases', + str(OPEN_API_DATA_PATH / 'not.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.ERROR + + +def test_main_with_more_bad_aliases(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--aliases', + str(OPEN_API_DATA_PATH / 'list.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.ERROR + + +def test_main_with_bad_extra_data(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--extra-template-data', + str(OPEN_API_DATA_PATH / 'not.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.ERROR + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_with_snake_case_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--snake-case-field', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'with_snake_case_field.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_with_strip_default_none(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--strip-default-none', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'with_strip_default_none.py').read_text() + ) + + +def test_disable_timestamp(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--disable-timestamp', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'disable_timestamp.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_enable_version_header(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--enable-version-header', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'enable_version_header.py').read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'allow_population_by_field_name.py', + ), + ( + 'pydantic_v2.BaseModel', + 'allow_population_by_field_name_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_allow_population_by_field_name(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--allow-population-by-field-name', + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'allow_extra_fields.py', + ), + ( + 'pydantic_v2.BaseModel', + 'allow_extra_fields_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_allow_extra_fields(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--allow-extra-fields', + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'enable_faux_immutability.py', + ), + ( + 'pydantic_v2.BaseModel', + 'enable_faux_immutability_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_enable_faux_immutability(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--enable-faux-immutability', + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_use_default(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--use-default', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'use_default.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_force_optional(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--force-optional', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'force_optional.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_with_exclusive(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'exclusive.yaml'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'with_exclusive.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_subclass_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'subclass_enum.json'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'subclass_enum.py').read_text() + ) + + +def test_main_use_standard_collections(tmpdir_factory: TempdirFactory) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--use-standard-collections', + ] + ) + main_use_standard_collections_dir = ( + EXPECTED_OPENAPI_PATH / 'use_standard_collections' + ) + for path in main_use_standard_collections_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_use_standard_collections_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +def test_main_use_generic_container_types(tmpdir_factory: TempdirFactory) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--use-generic-container-types', + ] + ) + main_use_generic_container_types_dir = ( + EXPECTED_OPENAPI_PATH / 'use_generic_container_types' + ) + for path in main_use_generic_container_types_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_use_generic_container_types_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@pytest.mark.benchmark +def test_main_use_generic_container_types_standard_collections( + tmpdir_factory: TempdirFactory, +) -> None: + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--use-generic-container-types', + '--use-standard-collections', + ] + ) + main_use_generic_container_types_standard_collections_dir = ( + EXPECTED_OPENAPI_PATH / 'use_generic_container_types_standard_collections' + ) + for path in main_use_generic_container_types_standard_collections_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_use_generic_container_types_standard_collections_dir) + ).read_text() + assert result == path.read_text() + + +def test_main_use_generic_container_types_py36(capsys) -> None: + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + + return_code: Exit = main( + [ + '--input', + str(input_filename), + '--use-generic-container-types', + '--target-python-version', + '3.6', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err == '`--use-generic-container-types` can not be used with ' + '`--target-python_version` 3.6.\n ' + 'The version will be not supported in a future version\n' + ) + + +def test_main_original_field_name_delimiter_without_snake_case_field(capsys) -> None: + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + + return_code: Exit = main( + [ + '--input', + str(input_filename), + '--original-field-name-delimiter', + '-', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err + == '`--original-field-name-delimiter` can not be used without `--snake-case-field`.\n' + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'datetime.py', + ), + ( + 'pydantic_v2.BaseModel', + 'datetime_pydantic_v2.py', + ), + ], +) +def test_main_openapi_datetime(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'datetime.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_models_not_found(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'no_components.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert captured.err == 'Models not found in the input data\n' + + +@pytest.mark.skipif( + version.parse(pydantic.VERSION) < version.parse('1.9.0'), + reason='Require Pydantic version 1.9.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_openapi_enum_models_as_literal_one(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'enum_models.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--enum-field-as-literal', + 'one', + '--target-python-version', + '3.8', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'enum_models' / 'one.py').read_text() + ) + + +@pytest.mark.skipif( + version.parse(pydantic.VERSION) < version.parse('1.9.0'), + reason='Require Pydantic version 1.9.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_openapi_use_one_literal_as_default(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'enum_models.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--enum-field-as-literal', + 'one', + '--target-python-version', + '3.8', + '--use-one-literal-as-default', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'enum_models' / 'one_literal_as_default.py' + ).read_text() + ) + + +@pytest.mark.skipif( + version.parse(pydantic.VERSION) < version.parse('1.9.0'), + reason='Require Pydantic version 1.9.0 or later ', +) +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@freeze_time('2019-07-26') +def test_main_openapi_enum_models_as_literal_all(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'enum_models.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--enum-field-as-literal', + 'all', + '--target-python-version', + '3.8', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'enum_models' / 'all.py').read_text() + ) + + +@pytest.mark.skipif( + version.parse(pydantic.VERSION) < version.parse('1.9.0'), + reason='Require Pydantic version 1.9.0 or later ', +) +@pytest.mark.skipif( + black.__version__.split('.')[0] >= '24', + reason="Installed black doesn't support the old style", +) +@freeze_time('2019-07-26') +def test_main_openapi_enum_models_as_literal_py37(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'enum_models.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--enum-field-as-literal', + 'all', + ] + ) + + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'enum_models' / 'as_literal_py37.py' + ).read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_openapi_all_of_required(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'allof_required.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'allof_required.py').read_text() + ) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_openapi_nullable(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'nullable.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_nullable_strict_nullable(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--strict-nullable', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'nullable_strict_nullable.py').read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'general.py', + ), + ( + 'pydantic_v2.BaseModel', + 'pydantic_v2.py', + ), + ( + 'msgspec.Struct', + 'msgspec_pattern.py', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_pattern(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'pattern.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--target-python', + '3.9', + '--output-model-type', + output_model, + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_OPENAPI_PATH / 'pattern' / expected_output + ).read_text().replace('pattern.json', 'pattern.yaml') + + +@pytest.mark.parametrize( + 'expected_output, args', + [ + ('pattern_with_lookaround_pydantic_v2.py', []), + ( + 'pattern_with_lookaround_pydantic_v2_field_constraints.py', + ['--field-constraints'], + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] < '22', + reason="Installed black doesn't support Python version 3.10", +) +def test_main_openapi_pattern_with_lookaround_pydantic_v2( + expected_output: str, args: List[str] +): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'pattern_lookaround.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--target-python', + '3.9', + '--output-model-type', + 'pydantic_v2.BaseModel', + *args, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_generate_custom_class_name_generator_modular( + tmpdir_factory: TempdirFactory, +): + output_directory = Path(tmpdir_factory.mktemp('output')) + + output_path = output_directory / 'model' + main_modular_custom_class_name_dir = ( + EXPECTED_OPENAPI_PATH / 'modular_custom_class_name' + ) + + def custom_class_name_generator(name): + return f'Custom{name[0].upper() + name[1:]}' + + with freeze_time(TIMESTAMP): + input_ = (OPEN_API_DATA_PATH / 'modular.yaml').relative_to(Path.cwd()) + assert not input_.is_absolute() + generate( + input_=input_, + input_file_type=InputFileType.OpenAPI, + output=output_path, + custom_class_name_generator=custom_class_name_generator, + ) + + for path in main_modular_custom_class_name_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_custom_class_name_dir) + ).read_text() + assert result == path.read_text() + + +@freeze_time('2019-07-26') +def test_main_http_openapi(mocker): + def get_mock_response(path: str) -> mocker.Mock: + mock = mocker.Mock() + mock.text = (OPEN_API_DATA_PATH / path).read_text() + return mock + + httpx_get_mock = mocker.patch( + 'httpx.get', + side_effect=[ + get_mock_response('refs.yaml'), + get_mock_response('definitions.yaml'), + ], + ) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--url', + 'https://example.com/refs.yaml', + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'http_refs.py').read_text() + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/refs.yaml', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + call( + 'https://teamdigitale.github.io/openapi/0.0.6/definitions.yaml', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@freeze_time('2019-07-26') +def test_main_disable_appending_item_suffix(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--field-constraints', + '--disable-appending-item-suffix', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'disable_appending_item_suffix.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_body_and_parameters(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--openapi-scopes', + 'paths', + 'schemas', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'body_and_parameters' / 'general.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_body_and_parameters_remote_ref(mocker): + input_path = OPEN_API_DATA_PATH / 'body_and_parameters_remote_ref.yaml' + person_response = mocker.Mock() + person_response.text = input_path.read_text() + httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) + + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(input_path), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--openapi-scopes', + 'paths', + 'schemas', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'body_and_parameters' / 'remote_ref.py' + ).read_text() + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://schema.example', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_body_and_parameters_only_paths(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--openapi-scopes', + 'paths', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'body_and_parameters' / 'only_paths.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_body_and_parameters_only_schemas(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--openapi-scopes', + 'schemas', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'body_and_parameters' / 'only_schemas.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_content_in_parameters(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'content_in_parameters.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'content_in_parameters.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_oas_response_reference(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'oas_response_reference.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--openapi-scopes', + 'paths', + 'schemas', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'oas_response_reference.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_json_pointer(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'json_pointer.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'json_pointer.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ('pydantic.BaseModel', 'use_annotated_with_field_constraints.py'), + ( + 'pydantic_v2.BaseModel', + 'use_annotated_with_field_constraints_pydantic_v2.py', + ), + ], +) +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_use_annotated_with_field_constraints(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--field-constraints', + '--use-annotated', + '--target-python-version', + '3.9', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_use_annotated_with_field_constraints_py38(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--use-annotated', + '--target-python-version', + '3.8', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'use_annotated_with_field_constraints_py38.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_nested_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nested_enum.json'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'nested_enum.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_openapi_special_yaml_keywords(mocker): + mock_prance = mocker.patch('prance.BaseParser') + + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'special_yaml_keywords.yaml'), + '--output', + str(output_file), + '--validation', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'special_yaml_keywords.py').read_text() + ) + mock_prance.assert_called_once() + + +@pytest.mark.skipif( + black.__version__.split('.')[0] < '22', + reason="Installed black doesn't support Python version 3.10", +) +@freeze_time('2019-07-26') +def test_main_openapi_nullable_use_union_operator(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--use-union-operator', + '--strict-nullable', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'nullable_strict_nullable_use_union_operator.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_external_relative_ref(): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'external_relative_ref' / 'model_b'), + '--output', + str(output_path), + ] + ) + assert return_code == Exit.OK + main_modular_dir = EXPECTED_OPENAPI_PATH / 'external_relative_ref' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text() + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_collapse_root_models(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'not_real_string.json'), + '--output', + str(output_file), + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'collapse_root_models.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_collapse_root_models_field_constraints(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'not_real_string.json'), + '--output', + str(output_file), + '--collapse-root-models', + '--field-constraints', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'collapse_root_models_field_constraints.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_collapse_root_models_with_references_to_flat_types(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'flat_type.jsonschema'), + '--output', + str(output_file), + '--collapse-root-models', + ] + ) + + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH + / 'collapse_root_models_with_references_to_flat_types.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_max_items_enum(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'max_items_enum.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'max_items_enum.py').read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'const.py', + ), + ( + 'pydantic_v2.BaseModel', + 'const_pydantic_v2.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_openapi_const(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'const.json'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--output-model', + output_model, + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'const_field.py', + ), + ( + 'pydantic_v2.BaseModel', + 'const_field_pydantic_v2.py', + ), + ( + 'msgspec.Struct', + 'const_field_msgspec.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_openapi_const_field(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'const.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--output-model', + output_model, + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / expected_output).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_complex_reference(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'complex_reference.json'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'complex_reference.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_reference_to_object_properties(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'reference_to_object_properties.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'reference_to_object_properties.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_reference_to_object_properties_collapse_root_models(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'reference_to_object_properties.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH + / 'reference_to_object_properties_collapse_root_models.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_override_required_all_of_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'override_required_all_of.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'override_required_all_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_use_default_kwarg(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--use-default-kwarg', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'use_default_kwarg.py').read_text() + ) + + +@pytest.mark.parametrize( + 'input,output', + [ + ( + 'discriminator.yaml', + 'general.py', + ), + ( + 'discriminator_without_mapping.yaml', + 'without_mapping.py', + ), + ], +) +@freeze_time('2019-07-26') +def test_main_openapi_discriminator(input, output): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / input), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'discriminator' / output).read_text() + ) + + +@freeze_time('2023-07-27') +@pytest.mark.parametrize( + 'kind,option, expected', + [ + ( + 'anyOf', + '--collapse-root-models', + 'in_array_collapse_root_models.py', + ), + ( + 'oneOf', + '--collapse-root-models', + 'in_array_collapse_root_models.py', + ), + ('anyOf', None, 'in_array.py'), + ('oneOf', None, 'in_array.py'), + ], +) +def test_main_openapi_discriminator_in_array(kind, option, expected): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + input_file = f'discriminator_in_array_{kind.lower()}.yaml' + return_code: Exit = main( + [ + a + for a in [ + '--input', + str(OPEN_API_DATA_PATH / input_file), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + option, + ] + if a + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_OPENAPI_PATH / 'discriminator' / expected + ).read_text().replace('discriminator_in_array.yaml', input_file) + + +@pytest.mark.parametrize( + 'output_model,expected_output', + [ + ( + 'pydantic.BaseModel', + 'default_object', + ), + ( + 'pydantic_v2.BaseModel', + 'pydantic_v2_default_object', + ), + ( + 'msgspec.Struct', + 'msgspec_default_object', + ), + ], +) +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_default_object(output_model, expected_output): + with TemporaryDirectory() as output_dir: + output_path: Path = Path(output_dir) + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'default_object.yaml'), + '--output', + str(output_dir), + '--output-model', + output_model, + '--input-file-type', + 'openapi', + '--target-python-version', + '3.9', + ] + ) + assert return_code == Exit.OK + + main_modular_dir = EXPECTED_OPENAPI_PATH / expected_output + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath( + path.relative_to(main_modular_dir) + ).read_text() + assert result == path.read_text(), path + + +@freeze_time('2019-07-26') +def test_main_dataclass(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'dataclass.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_dataclass_base_class(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + '--base-class', + 'custom_base.Base', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'dataclass_base_class.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_reference_same_hierarchy_directory(): + with TemporaryDirectory() as output_dir: + with chdir(OPEN_API_DATA_PATH / 'reference_same_hierarchy_directory'): + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + './public/entities.yaml', + '--output', + str(output_file), + '--input-file-type', + 'openapi', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'reference_same_hierarchy_directory.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_multiple_required_any_of(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'multiple_required_any_of.yaml'), + '--output', + str(output_file), + '--collapse-root-models', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'multiple_required_any_of.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_max_min(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'max_min_number.yaml'), + '--output', + str(output_file), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'max_min_number.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_use_operation_id_as_name(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--use-operation-id-as-name', + '--openapi-scopes', + 'paths', + 'schemas', + 'parameters', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'use_operation_id_as_name.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_use_operation_id_as_name_not_found_operation_id(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--use-operation-id-as-name', + '--openapi-scopes', + 'paths', + 'schemas', + 'parameters', + ] + ) + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err + == 'All operations must have an operationId when --use_operation_id_as_name is set.' + 'The following path was missing an operationId: pets\n' + ) + + +@freeze_time('2019-07-26') +def test_main_unsorted_optional_fields(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'unsorted_optional_fields.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'dataclasses.dataclass', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'unsorted_optional_fields.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_typed_dict(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'typed_dict.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_typed_dict_py_38(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.8', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'typed_dict_py_38.py').read_text() + ) + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +def test_main_modular_typed_dict(tmpdir_factory: TempdirFactory) -> None: + """Test main function on modular file.""" + + output_directory = Path(tmpdir_factory.mktemp('output')) + + input_filename = OPEN_API_DATA_PATH / 'modular.yaml' + output_path = output_directory / 'model' + + with freeze_time(TIMESTAMP): + main( + [ + '--input', + str(input_filename), + '--output', + str(output_path), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + ] + ) + main_modular_dir = EXPECTED_OPENAPI_PATH / 'modular_typed_dict' + for path in main_modular_dir.rglob('*.py'): + result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() + assert result == path.read_text() + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_typed_dict_nullable(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'typed_dict_nullable.py').read_text() + ) + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_typed_dict_nullable_strict_nullable(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'nullable.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + '--strict-nullable', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'typed_dict_nullable_strict_nullable.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_custom_file_header_path(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--custom-file-header-path', + str(DATA_PATH / 'custom_file_header.txt'), + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'custom_file_header.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_custom_file_header_duplicate_options(capsys): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--custom-file-header-path', + str(DATA_PATH / 'custom_file_header.txt'), + '--custom-file-header', + 'abc', + ] + ) + + captured = capsys.readouterr() + assert return_code == Exit.ERROR + assert ( + captured.err + == '`--custom_file_header_path` can not be used with `--custom_file_header`.\n' + ) + + +@freeze_time('2019-07-26') +def test_main_pydantic_v2(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'pydantic_v2.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_custom_id_pydantic_v2(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'custom_id.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'custom_id_pydantic_v2.py').read_text() + ) + + +@pytest.mark.skipif( + not isort.__version__.startswith('4.'), + reason="isort 5.x don't sort pydantic modules", +) +@freeze_time('2019-07-26') +def test_main_openapi_custom_id_pydantic_v2_custom_base(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'custom_id.yaml'), + '--output', + str(output_file), + '--output-model-type', + 'pydantic_v2.BaseModel', + '--base-class', + 'custom_base.Base', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH / 'custom_id_pydantic_v2_custom_base.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_all_of_with_relative_ref(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'all_of_with_relative_ref' / 'openapi.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'openapi', + '--output-model-type', + 'pydantic_v2.BaseModel', + '--keep-model-order', + '--collapse-root-models', + '--field-constraints', + '--use-title-as-name', + '--field-include-all-keys', + '--use-field-description', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'all_of_with_relative_ref.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_msgspec_struct(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api.yaml'), + '--output', + str(output_file), + # min msgspec python version is 3.8 + '--target-python-version', + '3.8', + '--output-model-type', + 'msgspec.Struct', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'msgspec_struct.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_openapi_msgspec_struct_snake_case(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_ordered_required_fields.yaml'), + '--output', + str(output_file), + # min msgspec python version is 3.8 + '--target-python-version', + '3.8', + '--snake-case-field', + '--output-model-type', + 'msgspec.Struct', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_OPENAPI_PATH / 'msgspec_struct_snake_case.py').read_text() + ) + + +@freeze_time('2019-07-26') +@pytest.mark.skipif( + black.__version__.split('.')[0] == '19', + reason="Installed black doesn't support the old style", +) +def test_main_openapi_msgspec_use_annotated_with_field_constraints(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), + '--output', + str(output_file), + '--field-constraints', + '--target-python-version', + '3.9', + '--output-model-type', + 'msgspec.Struct', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_OPENAPI_PATH + / 'msgspec_use_annotated_with_field_constraints.py' + ).read_text() + ) diff --git a/tests/main/test_main_csv.py b/tests/main/test_main_csv.py new file mode 100644 index 000000000..e333a999b --- /dev/null +++ b/tests/main/test_main_csv.py @@ -0,0 +1,62 @@ +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory + +import pytest +from freezegun import freeze_time + +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH + +MonkeyPatch = pytest.MonkeyPatch + +CSV_DATA_PATH: Path = DATA_PATH / 'csv' +EXPECTED_CSV_PATH: Path = EXPECTED_MAIN_PATH / 'csv' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@freeze_time('2019-07-26') +def test_csv_file(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(CSV_DATA_PATH / 'simple.csv'), + '--output', + str(output_file), + '--input-file-type', + 'csv', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_CSV_PATH / 'csv_file_simple.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_csv_stdin(monkeypatch): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + monkeypatch.setattr('sys.stdin', (CSV_DATA_PATH / 'simple.csv').open()) + return_code: Exit = main( + [ + '--output', + str(output_file), + '--input-file-type', + 'csv', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_CSV_PATH / 'csv_stdin_simple.py').read_text() + ) diff --git a/tests/main/test_main_general.py b/tests/main/test_main_general.py new file mode 100644 index 000000000..60fd48551 --- /dev/null +++ b/tests/main/test_main_general.py @@ -0,0 +1,88 @@ +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory + +import pytest +from freezegun import freeze_time + +from datamodel_code_generator import ( + snooper_to_methods, +) +from datamodel_code_generator.__main__ import Exit, main + +CaptureFixture = pytest.CaptureFixture +MonkeyPatch = pytest.MonkeyPatch + +DATA_PATH: Path = Path(__file__).parent.parent / 'data' +PYTHON_DATA_PATH: Path = DATA_PATH / 'python' +EXPECTED_MAIN_PATH = DATA_PATH / 'expected' / 'main' + +TIMESTAMP = '1985-10-26T01:21:00-07:00' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +def test_debug(mocker) -> None: + with pytest.raises(expected_exception=SystemExit): + main(['--debug', '--help']) + + mocker.patch('datamodel_code_generator.pysnooper', None) + with pytest.raises(expected_exception=SystemExit): + main(['--debug', '--help']) + + +@freeze_time('2019-07-26') +def test_snooper_to_methods_without_pysnooper(mocker) -> None: + mocker.patch('datamodel_code_generator.pysnooper', None) + mock = mocker.Mock() + assert snooper_to_methods()(mock) == mock + + +@pytest.mark.parametrize(argnames='no_color', argvalues=[False, True]) +def test_show_help(no_color: bool, capsys: CaptureFixture[str]): + args = ['--no-color'] if no_color else [] + args += ['--help'] + + with pytest.raises(expected_exception=SystemExit): + return_code: Exit = main(args) + assert return_code == Exit.OK + + output = capsys.readouterr().out + assert ('\x1b' not in output) == no_color + + +def test_show_help_when_no_input(mocker): + print_help_mock = mocker.patch( + 'datamodel_code_generator.__main__.arg_parser.print_help' + ) + isatty_mock = mocker.patch('sys.stdin.isatty', return_value=True) + return_code: Exit = main([]) + assert return_code == Exit.ERROR + assert isatty_mock.called + assert print_help_mock.called + + +@freeze_time('2019-07-26') +def test_space_and_special_characters_dict(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(PYTHON_DATA_PATH / 'space_and_special_characters_dict.py'), + '--output', + str(output_file), + '--input-file-type', + 'dict', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_MAIN_PATH / 'space_and_special_characters_dict.py').read_text() + ) diff --git a/tests/main/test_main_json.py b/tests/main/test_main_json.py new file mode 100644 index 000000000..eb6c877d9 --- /dev/null +++ b/tests/main/test_main_json.py @@ -0,0 +1,249 @@ +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import call + +import black +import pytest +from freezegun import freeze_time +from packaging import version + +from datamodel_code_generator import ( + chdir, +) +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH + +CaptureFixture = pytest.CaptureFixture +FixtureRequest = pytest.FixtureRequest +MonkeyPatch = pytest.MonkeyPatch + +JSON_DATA_PATH: Path = DATA_PATH / 'json' +EXPECTED_JSON_PATH: Path = EXPECTED_MAIN_PATH / 'json' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@freeze_time('2019-07-26') +def test_main_json(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'pet.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() == (EXPECTED_JSON_PATH / 'general.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_space_and_special_characters_json(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'space_and_special_characters.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_PATH / 'space_and_special_characters.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_failed(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'broken.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.ERROR + + +@freeze_time('2019-07-26') +def test_main_json_array_include_null(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'array_include_null.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_PATH / 'json_array_include_null.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_reuse_model(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'duplicate_models.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + '--reuse-model', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_PATH / 'json_reuse_model.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_simple_json_snake_case_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + with chdir(JSON_DATA_PATH / 'simple.json'): + return_code: Exit = main( + [ + '--input', + 'simple.json', + '--output', + str(output_file), + '--input-file-type', + 'json', + '--snake-case-field', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_PATH / 'simple_json_snake_case_field.py').read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_http_json(mocker): + def get_mock_response(path: str) -> mocker.Mock: + mock = mocker.Mock() + mock.text = (JSON_DATA_PATH / path).read_text() + return mock + + httpx_get_mock = mocker.patch( + 'httpx.get', + side_effect=[ + get_mock_response('pet.json'), + ], + ) + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--url', + 'https://example.com/pet.json', + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == ( + EXPECTED_JSON_PATH / 'general.py' + ).read_text().replace( + '# filename: pet.json', + '# filename: https://example.com/pet.json', + ) + httpx_get_mock.assert_has_calls( + [ + call( + 'https://example.com/pet.json', + headers=None, + verify=True, + follow_redirects=True, + params=None, + ), + ] + ) + + +@pytest.mark.skipif( + version.parse(black.__version__) < version.parse('23.3.0'), + reason='Require Black version 23.3.0 or later ', +) +@freeze_time('2019-07-26') +def test_main_typed_dict_space_and_special_characters(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'space_and_special_characters.json'), + '--output', + str(output_file), + '--output-model-type', + 'typing.TypedDict', + '--target-python-version', + '3.11', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_JSON_PATH / 'typed_dict_space_and_special_characters.py' + ).read_text() + ) + + +@freeze_time('2019-07-26') +def test_main_json_snake_case_field(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'snake_case.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + '--snake-case-field', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == (EXPECTED_JSON_PATH / 'json_snake_case_field.py').read_text() + ) diff --git a/tests/main/test_main_yaml.py b/tests/main/test_main_yaml.py new file mode 100644 index 000000000..3593020cb --- /dev/null +++ b/tests/main/test_main_yaml.py @@ -0,0 +1,39 @@ +from argparse import Namespace +from pathlib import Path +from tempfile import TemporaryDirectory + +import pytest +from freezegun import freeze_time + +from datamodel_code_generator.__main__ import Exit, main +from tests.main.test_main_general import DATA_PATH, EXPECTED_MAIN_PATH + +MonkeyPatch = pytest.MonkeyPatch + +YAML_DATA_PATH: Path = DATA_PATH / 'yaml' + + +@pytest.fixture(autouse=True) +def reset_namespace(monkeypatch: MonkeyPatch): + namespace_ = Namespace(no_color=False) + monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) + monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) + + +@pytest.mark.benchmark +@freeze_time('2019-07-26') +def test_main_yaml(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(YAML_DATA_PATH / 'pet.yaml'), + '--output', + str(output_file), + '--input-file-type', + 'yaml', + ] + ) + assert return_code == Exit.OK + assert output_file.read_text() == (EXPECTED_MAIN_PATH / 'yaml.py').read_text() diff --git a/tests/test_infer_input_type.py b/tests/test_infer_input_type.py index 3254a87f6..05a082aab 100644 --- a/tests/test_infer_input_type.py +++ b/tests/test_infer_input_type.py @@ -22,12 +22,12 @@ def assert_infer_input_type(file: Path, raw_data_type: InputFileType) -> None: for file in (DATA_PATH / 'jsonschema').rglob('*'): if str(file).endswith(('external_child.json', 'external_child.yaml')): continue - if 'reference_same_hierarchy_directory' in str(file): - continue assert_infer_input_type(file, InputFileType.JsonSchema) for file in (DATA_PATH / 'openapi').rglob('*'): if 'all_of_with_relative_ref' in str(file): continue + if 'reference_same_hierarchy_directory' in str(file): + continue if str(file).endswith( ( 'aliases.json', diff --git a/tests/test_main.py b/tests/test_main.py deleted file mode 100644 index 42aead56e..000000000 --- a/tests/test_main.py +++ /dev/null @@ -1,6980 +0,0 @@ -import json -import platform -import shutil -from argparse import Namespace -from pathlib import Path -from tempfile import TemporaryDirectory -from typing import List -from unittest.mock import call - -import black -import isort -import pydantic -import pytest -from freezegun import freeze_time -from packaging import version - -from datamodel_code_generator import ( - DataModelType, - InputFileType, - chdir, - generate, - inferred_message, - snooper_to_methods, -) -from datamodel_code_generator.__main__ import Exit, main - -try: - from pytest import TempdirFactory -except ImportError: - from _pytest.tmpdir import TempdirFactory - -CaptureFixture = pytest.CaptureFixture -FixtureRequest = pytest.FixtureRequest -MonkeyPatch = pytest.MonkeyPatch - -DATA_PATH: Path = Path(__file__).parent / 'data' -OPEN_API_DATA_PATH: Path = DATA_PATH / 'openapi' -JSON_SCHEMA_DATA_PATH: Path = DATA_PATH / 'jsonschema' -JSON_DATA_PATH: Path = DATA_PATH / 'json' -YAML_DATA_PATH: Path = DATA_PATH / 'yaml' -PYTHON_DATA_PATH: Path = DATA_PATH / 'python' -CSV_DATA_PATH: Path = DATA_PATH / 'csv' -GRAPHQL_DATA_PATH: Path = DATA_PATH / 'graphql' -EXPECTED_MAIN_PATH = DATA_PATH / 'expected' / 'main' - -TIMESTAMP = '1985-10-26T01:21:00-07:00' - - -@pytest.fixture(autouse=True) -def reset_namespace(monkeypatch: MonkeyPatch): - namespace_ = Namespace(no_color=False) - monkeypatch.setattr('datamodel_code_generator.__main__.namespace', namespace_) - monkeypatch.setattr('datamodel_code_generator.arguments.namespace', namespace_) - - -def test_debug(mocker) -> None: - with pytest.raises(expected_exception=SystemExit): - main(['--debug', '--help']) - - mocker.patch('datamodel_code_generator.pysnooper', None) - with pytest.raises(expected_exception=SystemExit): - main(['--debug', '--help']) - - -@freeze_time('2019-07-26') -def test_snooper_to_methods_without_pysnooper(mocker) -> None: - mocker.patch('datamodel_code_generator.pysnooper', None) - mock = mocker.Mock() - assert snooper_to_methods()(mock) == mock - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_inheritance_forward_ref(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'inheritance_forward_ref.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_inheritance_forward_ref' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_inheritance_forward_ref_keep_model_order(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'inheritance_forward_ref.json'), - '--output', - str(output_file), - '--keep-model-order', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_inheritance_forward_ref_keep_model_order' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main' / 'output.py').read_text() - ) - - -@pytest.mark.skip(reason='pytest-xdist does not support the test') -@freeze_time('2019-07-26') -def test_main_without_arguments(): - with pytest.raises(SystemExit): - main() - - -@freeze_time('2019-07-26') -def test_main_pydantic_basemodel(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_base_class(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml') - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--base-class', - 'custom_module.Base', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_base_class' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_target_python_version(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--target-python-version', - '3.6', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'target_python_version' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_autodetect(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'auto', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_autodetect' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_autodetect_failed(): - with TemporaryDirectory() as input_dir, TemporaryDirectory() as output_dir: - input_file: Path = Path(input_dir) / 'input.yaml' - output_file: Path = Path(output_dir) / 'output.py' - - input_file.write_text(':') - - return_code: Exit = main( - [ - '--input', - str(input_file), - '--output', - str(output_file), - '--input-file-type', - 'auto', - ] - ) - assert return_code == Exit.ERROR - - -@freeze_time('2019-07-26') -def test_main_jsonschema(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_jsonschema' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_nested_deep(): - with TemporaryDirectory() as output_dir: - output_init_file: Path = Path(output_dir) / '__init__.py' - output_nested_file: Path = Path(output_dir) / 'nested/deep.py' - output_empty_parent_nested_file: Path = ( - Path(output_dir) / 'empty_parent/nested/deep.py' - ) - - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nested_person.json'), - '--output', - str(output_dir), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_init_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_nested_deep' / '__init__.py' - ).read_text() - ) - - assert ( - output_nested_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_nested_deep' - / 'nested' - / 'deep.py' - ).read_text() - ) - assert ( - output_empty_parent_nested_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_nested_deep' - / 'empty_parent' - / 'nested' - / 'deep.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_nested_skip(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nested_skip.json'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - nested_skip_dir = EXPECTED_MAIN_PATH / 'main_jsonschema_nested_skip' - for path in nested_skip_dir.rglob('*.py'): - result = output_path.joinpath(path.relative_to(nested_skip_dir)).read_text() - assert result == path.read_text() - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_external_files(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'external_parent_root.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_external_files' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_multiple_files(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'multiple_files'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'multiple_files' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_json(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'pet.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_json' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_space_and_special_characters_json(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'space_and_special_characters.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'space_and_special_characters' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_failed(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'broken.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - ] - ) - assert return_code == Exit.ERROR - - -@freeze_time('2019-07-26') -def test_main_json_array_include_null(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'array_include_null.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_json_array_include_null' / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_null_and_array', - ), - ( - 'pydantic_v2.BaseModel', - 'main_null_and_array_v2', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_null_and_array(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'null_and_array.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_yaml(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(YAML_DATA_PATH / 'pet.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'yaml', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_yaml' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -def test_main_modular(tmpdir_factory: TempdirFactory) -> None: - """Test main function on modular file.""" - - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main(['--input', str(input_filename), '--output', str(output_path)]) - main_modular_dir = EXPECTED_MAIN_PATH / 'main_modular' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() - assert result == path.read_text() - - -def test_main_modular_reuse_model(tmpdir_factory: TempdirFactory) -> None: - """Test main function on modular file.""" - - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--reuse-model', - ] - ) - main_modular_dir = EXPECTED_MAIN_PATH / 'main_modular_reuse_model' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() - assert result == path.read_text() - - -def test_main_modular_no_file() -> None: - """Test main function on modular file with no output name.""" - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - - assert main(['--input', str(input_filename)]) == Exit.ERROR - - -def test_main_modular_filename(tmpdir_factory: TempdirFactory) -> None: - """Test main function on modular file with filename.""" - - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_filename = output_directory / 'model.py' - - assert ( - main(['--input', str(input_filename), '--output', str(output_filename)]) - == Exit.ERROR - ) - - -def test_main_no_file(capsys: CaptureFixture) -> None: - """Test main function on non-modular file with no output name.""" - - input_filename = OPEN_API_DATA_PATH / 'api.yaml' - - with freeze_time(TIMESTAMP): - main(['--input', str(input_filename)]) - - captured = capsys.readouterr() - assert ( - captured.out == (EXPECTED_MAIN_PATH / 'main_no_file' / 'output.py').read_text() - ) - assert captured.err == inferred_message.format('openapi') + '\n' - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_extra_template_data_config', - ), - ( - 'pydantic_v2.BaseModel', - 'main_extra_template_data_config_pydantic_v2', - ), - ], -) -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_extra_template_data_config( - capsys: CaptureFixture, output_model, expected_output -) -> None: - """Test main function with custom config data in extra template.""" - - input_filename = OPEN_API_DATA_PATH / 'api.yaml' - extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--extra-template-data', - str(extra_template_data), - '--output-model', - output_model, - ] - ) - - captured = capsys.readouterr() - assert ( - captured.out == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - assert captured.err == inferred_message.format('openapi') + '\n' - - -def test_main_custom_template_dir_old_style(capsys: CaptureFixture) -> None: - """Test main function with custom template directory.""" - - input_filename = OPEN_API_DATA_PATH / 'api.yaml' - custom_template_dir = DATA_PATH / 'templates_old_style' - extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--custom-template-dir', - str(custom_template_dir), - '--extra-template-data', - str(extra_template_data), - ] - ) - - captured = capsys.readouterr() - assert ( - captured.out - == (EXPECTED_MAIN_PATH / 'main_custom_template_dir' / 'output.py').read_text() - ) - assert captured.err == inferred_message.format('openapi') + '\n' - - -def test_main_custom_template_dir(capsys: CaptureFixture) -> None: - """Test main function with custom template directory.""" - - input_filename = OPEN_API_DATA_PATH / 'api.yaml' - custom_template_dir = DATA_PATH / 'templates' - extra_template_data = OPEN_API_DATA_PATH / 'extra_data.json' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--custom-template-dir', - str(custom_template_dir), - '--extra-template-data', - str(extra_template_data), - ] - ) - - captured = capsys.readouterr() - assert ( - captured.out - == (EXPECTED_MAIN_PATH / 'main_custom_template_dir' / 'output.py').read_text() - ) - assert captured.err == inferred_message.format('openapi') + '\n' - - -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@freeze_time('2019-07-26') -def test_pyproject(): - if platform.system() == 'Windows': - - def get_path(path): - return str(path).replace('\\', '\\\\') - - else: - - def get_path(path): - return str(path) - - with TemporaryDirectory() as output_dir: - output_dir = Path(output_dir) - - with chdir(output_dir): - output_file: Path = output_dir / 'output.py' - pyproject_toml_path = Path(DATA_PATH) / 'project' / 'pyproject.toml' - pyproject_toml = ( - pyproject_toml_path.read_text() - .replace('INPUT_PATH', get_path(OPEN_API_DATA_PATH / 'api.yaml')) - .replace('OUTPUT_PATH', get_path(output_file)) - .replace( - 'ALIASES_PATH', get_path(OPEN_API_DATA_PATH / 'empty_aliases.json') - ) - .replace( - 'EXTRA_TEMPLATE_DATA_PATH', - get_path(OPEN_API_DATA_PATH / 'empty_data.json'), - ) - .replace('CUSTOM_TEMPLATE_DIR_PATH', get_path(output_dir)) - ) - (output_dir / 'pyproject.toml').write_text(pyproject_toml) - - return_code: Exit = main([]) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'pyproject' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_pyproject_not_found(): - with TemporaryDirectory() as output_dir: - output_dir = Path(output_dir) - with chdir(output_dir): - output_file: Path = output_dir / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'pyproject_not_found' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_stdin(monkeypatch): - with TemporaryDirectory() as output_dir: - output_dir = Path(output_dir) - output_file: Path = output_dir / 'output.py' - monkeypatch.setattr('sys.stdin', (OPEN_API_DATA_PATH / 'api.yaml').open()) - return_code: Exit = main( - [ - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'stdin' / 'output.py').read_text() - ) - - -@pytest.mark.parametrize(argnames='no_color', argvalues=[False, True]) -def test_show_help(no_color: bool, capsys: CaptureFixture[str]): - args = ['--no-color'] if no_color else [] - args += ['--help'] - - with pytest.raises(expected_exception=SystemExit): - return_code: Exit = main(args) - assert return_code == Exit.OK - - output = capsys.readouterr().out - assert ('\x1b' not in output) == no_color - - -def test_show_help_when_no_input(mocker): - print_help_mock = mocker.patch( - 'datamodel_code_generator.__main__.arg_parser.print_help' - ) - isatty_mock = mocker.patch('sys.stdin.isatty', return_value=True) - return_code: Exit = main([]) - assert return_code == Exit.ERROR - assert isatty_mock.called - assert print_help_mock.called - - -@freeze_time('2019-07-26') -def test_validation(mocker): - mock_prance = mocker.patch('prance.BaseParser') - - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--validation', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'validation' / 'output.py').read_text() - ) - mock_prance.assert_called_once() - - -@freeze_time('2019-07-26') -def test_validation_failed(mocker): - mock_prance = mocker.patch('prance.BaseParser', side_effect=Exception('error')) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - assert ( - main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'invalid.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--validation', - ] - ) - == Exit.ERROR - ) - mock_prance.assert_called_once() - - -@pytest.mark.parametrize( - 'output_model,expected_output, args', - [ - ('pydantic.BaseModel', 'main_with_field_constraints', []), - ( - 'pydantic.BaseModel', - 'main_with_field_constraints_use_unique_items_as_set', - ['--use-unique-items-as-set'], - ), - ('pydantic_v2.BaseModel', 'main_with_field_constraints_pydantic_v2', []), - ( - 'pydantic_v2.BaseModel', - 'main_with_field_constraints_pydantic_v2_use_generic_container_types', - ['--use-generic-container-types'], - ), - ( - 'pydantic_v2.BaseModel', - 'main_with_field_constraints_pydantic_v2_use_generic_container_types_set', - ['--use-generic-container-types', '--use-unique-items-as-set'], - ), - ( - 'pydantic_v2.BaseModel', - 'main_with_field_constraints_pydantic_v2_use_standard_collections', - [ - '--use-standard-collections', - ], - ), - ( - 'pydantic_v2.BaseModel', - 'main_with_field_constraints_pydantic_v2_use_standard_collections_set', - ['--use-standard-collections', '--use-unique-items-as-set'], - ), - ], -) -@freeze_time('2019-07-26') -def test_main_with_field_constraints(output_model, expected_output, args): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--field-constraints', - '--output-model-type', - output_model, - *args, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_without_field_constraints', - ), - ( - 'pydantic_v2.BaseModel', - 'main_without_field_constraints_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_without_field_constraints(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_with_aliases', - ), - ( - 'msgspec.Struct', - 'main_with_aliases_msgspec', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_with_aliases(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--aliases', - str(OPEN_API_DATA_PATH / 'aliases.json'), - '--target-python', - '3.9', - '--output-model', - output_model, - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -def test_main_with_bad_aliases(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--aliases', - str(OPEN_API_DATA_PATH / 'not.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.ERROR - - -def test_main_with_more_bad_aliases(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--aliases', - str(OPEN_API_DATA_PATH / 'list.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.ERROR - - -def test_main_with_bad_extra_data(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--extra-template-data', - str(OPEN_API_DATA_PATH / 'not.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.ERROR - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_with_snake_case_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--snake-case-field', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_with_snake_case_field' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_with_strip_default_none(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--strip-default-none', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_with_strip_default_none' / 'output.py' - ).read_text() - ) - - -def test_disable_timestamp(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--disable-timestamp', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'disable_timestamp' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_enable_version_header(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--enable-version-header', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'enable_version_header' / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'allow_population_by_field_name', - ), - ( - 'pydantic_v2.BaseModel', - 'allow_population_by_field_name_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_allow_population_by_field_name(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--allow-population-by-field-name', - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'allow_extra_fields', - ), - ( - 'pydantic_v2.BaseModel', - 'allow_extra_fields_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_allow_extra_fields(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--allow-extra-fields', - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'enable_faux_immutability', - ), - ( - 'pydantic_v2.BaseModel', - 'enable_faux_immutability_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_enable_faux_immutability(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--enable-faux-immutability', - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_use_default(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--use-default', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'use_default' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_force_optional(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--force-optional', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'force_optional' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_use_default_pydantic_v2_with_json_schema_const(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'use_default_with_const.json'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - '--use-default', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'use_default_with_const' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_with_exclusive(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'exclusive.yaml'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_with_exclusive' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_subclass_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'subclass_enum.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_subclass_enum' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output,option', - [ - ( - 'pydantic.BaseModel', - 'main_complicated_enum_default_member', - '--set-default-enum-member', - ), - ( - 'dataclasses.dataclass', - 'main_complicated_enum_default_member_dataclass', - '--set-default-enum-member', - ), - ( - 'dataclasses.dataclass', - 'main_complicated_enum_default_member_dataclass', - None, - ), - ], -) -def test_main_complicated_enum_default_member(output_model, expected_output, option): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - a - for a in [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'complicated_enum.json'), - '--output', - str(output_file), - option, - '--output-model', - output_model, - ] - if a - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_json_reuse_enum_default_member(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'duplicate_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--reuse-model', - '--set-default-enum-member', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_json_reuse_enum_default_member' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_invalid_model_name_failed(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--class-name', - 'with', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err - == "title='with' is invalid class name. You have to set `--class-name` option\n" - ) - - -@freeze_time('2019-07-26') -def test_main_invalid_model_name_converted(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err - == "title='1Xyz' is invalid class name. You have to set `--class-name` option\n" - ) - - -@freeze_time('2019-07-26') -def test_main_invalid_model_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'invalid_model_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--class-name', - 'ValidModelName', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_invalid_model_name' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_with_local_file(mocker): - root_id_response = mocker.Mock() - root_id_response.text = 'dummy' - person_response = mocker.Mock() - person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'root_id.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_root_id' / 'output.py').read_text() - ) - httpx_get_mock.assert_not_called() - - -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_with_remote_file(mocker): - root_id_response = mocker.Mock() - root_id_response.text = 'dummy' - person_response = mocker.Mock() - person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - with TemporaryDirectory() as output_dir: - input_file = Path(output_dir, 'root_id.json') - shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id.json', input_file) - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(input_file), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_root_id' / 'output.py').read_text() - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/person.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_self_refs_with_local_file(mocker): - person_response = mocker.Mock() - person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'root_id_self_ref.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_root_id' / 'output.py' - ).read_text().replace( - 'filename: root_id.json', 'filename: root_id_self_ref.json' - ) - httpx_get_mock.assert_not_called() - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_self_refs_with_remote_file(mocker): - person_response = mocker.Mock() - person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - with TemporaryDirectory() as output_dir: - input_file = Path(output_dir, 'root_id_self_ref.json') - shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id_self_ref.json', input_file) - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(input_file), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_root_id' / 'output.py' - ).read_text().replace( - 'filename: root_id.json', 'filename: root_id_self_ref.json' - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/person.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_with_absolute_remote_file(mocker): - root_id_response = mocker.Mock() - root_id_response.text = 'dummy' - person_response = mocker.Mock() - person_response.text = (JSON_SCHEMA_DATA_PATH / 'person.json').read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - with TemporaryDirectory() as output_dir: - input_file = Path(output_dir, 'root_id_absolute_url.json') - shutil.copy(JSON_SCHEMA_DATA_PATH / 'root_id_absolute_url.json', input_file) - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(input_file), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_root_id_absolute_url' / 'output.py' - ).read_text() - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/person.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_root_id_jsonschema_with_absolute_local_file(mocker): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'root_id_absolute_url.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_root_id_absolute_url' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_id(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'id.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_jsonschema_id' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_id_as_stdin(monkeypatch): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - monkeypatch.setattr('sys.stdin', (JSON_SCHEMA_DATA_PATH / 'id.json').open()) - return_code: Exit = main( - [ - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_id_stdin' / 'output.py' - ).read_text() - ) - - -def test_main_jsonschema_ids(tmpdir_factory: TempdirFactory) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = JSON_SCHEMA_DATA_PATH / 'ids' / 'Organization.schema.json' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - main_jsonschema_ids_dir = EXPECTED_MAIN_PATH / 'main_jsonschema_ids' - for path in main_jsonschema_ids_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_jsonschema_ids_dir) - ).read_text() - assert result == path.read_text() - - -def test_main_use_standard_collections(tmpdir_factory: TempdirFactory) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--use-standard-collections', - ] - ) - main_use_standard_collections_dir = ( - EXPECTED_MAIN_PATH / 'main_use_standard_collections' - ) - for path in main_use_standard_collections_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_use_standard_collections_dir) - ).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -def test_main_use_generic_container_types(tmpdir_factory: TempdirFactory) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--use-generic-container-types', - ] - ) - main_use_generic_container_types_dir = ( - EXPECTED_MAIN_PATH / 'main_use_generic_container_types' - ) - for path in main_use_generic_container_types_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_use_generic_container_types_dir) - ).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@pytest.mark.benchmark -def test_main_use_generic_container_types_standard_collections( - tmpdir_factory: TempdirFactory, -) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--use-generic-container-types', - '--use-standard-collections', - ] - ) - main_use_generic_container_types_standard_collections_dir = ( - EXPECTED_MAIN_PATH / 'main_use_generic_container_types_standard_collections' - ) - for path in main_use_generic_container_types_standard_collections_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_use_generic_container_types_standard_collections_dir) - ).read_text() - assert result == path.read_text() - - -def test_main_use_generic_container_types_py36(capsys) -> None: - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - - return_code: Exit = main( - [ - '--input', - str(input_filename), - '--use-generic-container-types', - '--target-python-version', - '3.6', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err == '`--use-generic-container-types` can not be used with ' - '`--target-python_version` 3.6.\n ' - 'The version will be not supported in a future version\n' - ) - - -def test_main_original_field_name_delimiter_without_snake_case_field(capsys) -> None: - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - - return_code: Exit = main( - [ - '--input', - str(input_filename), - '--original-field-name-delimiter', - '-', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err - == '`--original-field-name-delimiter` can not be used without `--snake-case-field`.\n' - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_external_definitions(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'external_definitions_root.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_external_definitions' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_external_files_in_directory(tmpdir_factory: TempdirFactory) -> None: - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH - / 'external_files_in_directory' - / 'person.json' - ), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_external_files_in_directory' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_nested_directory(tmpdir_factory: TempdirFactory) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - output_path = output_directory / 'model' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'external_files_in_directory'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - main_nested_directory = EXPECTED_MAIN_PATH / 'main_nested_directory' - - for path in main_nested_directory.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_nested_directory) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_circular_reference(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'circular_reference.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_circular_reference' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_invalid_enum_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'invalid_enum_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_invalid_enum_name' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_invalid_enum_name_snake_case_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'invalid_enum_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--snake-case-field', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_invalid_enum_name_snake_case_field' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_reuse_model(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'duplicate_models.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - '--reuse-model', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_json_reuse_model' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_reuse_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'duplicate_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--reuse-model', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_json_reuse_enum' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_capitalise_enum_members(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'many_case_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--capitalise-enum-members', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_json_capitalise_enum_members' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_capitalise_enum_members_without_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--capitalise-enum-members', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_autodetect' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_openapi_datetime', - ), - ( - 'pydantic_v2.BaseModel', - 'main_openapi_datetime_pydantic_v2', - ), - ], -) -def test_main_openapi_datetime(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'datetime.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_similar_nested_array(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'similar_nested_array.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_similar_nested_array' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_space_and_special_characters_dict(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(PYTHON_DATA_PATH / 'space_and_special_characters_dict.py'), - '--output', - str(output_file), - '--input-file-type', - 'dict', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'space_and_special_characters_dict' / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_require_referenced_field', - ), - ( - 'pydantic_v2.BaseModel', - 'main_require_referenced_field_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_require_referenced_field(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_dir: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'require_referenced_field/'), - '--output', - str(output_dir), - '--input-file-type', - 'jsonschema', - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - - assert (output_dir / 'referenced.py').read_text() == ( - EXPECTED_MAIN_PATH / expected_output / 'referenced.py' - ).read_text() - assert (output_dir / 'required.py').read_text() == ( - EXPECTED_MAIN_PATH / expected_output / 'required.py' - ).read_text() - - -@freeze_time('2019-07-26') -def test_csv_file(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(CSV_DATA_PATH / 'simple.csv'), - '--output', - str(output_file), - '--input-file-type', - 'csv', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'csv_file_simple' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_csv_stdin(monkeypatch): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - monkeypatch.setattr('sys.stdin', (CSV_DATA_PATH / 'simple.csv').open()) - return_code: Exit = main( - [ - '--output', - str(output_file), - '--input-file-type', - 'csv', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'csv_stdin_simple' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_models_not_found(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'no_components.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert captured.err == 'Models not found in the input data\n' - - -@freeze_time('2019-07-26') -def test_main_json_pointer(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'json_pointer.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_json_pointer' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_nested_json_pointer(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nested_json_pointer.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_nested_json_pointer' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_multiple_files_json_pointer(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'multiple_files_json_pointer'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'multiple_files_json_pointer' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - version.parse(pydantic.VERSION) < version.parse('1.9.0'), - reason='Require Pydantic version 1.9.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_openapi_enum_models_as_literal_one(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'enum_models.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--enum-field-as-literal', - 'one', - '--target-python-version', - '3.8', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_enum_models_one' / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(pydantic.VERSION) < version.parse('1.9.0'), - reason='Require Pydantic version 1.9.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_openapi_use_one_literal_as_default(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'enum_models.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--enum-field-as-literal', - 'one', - '--target-python-version', - '3.8', - '--use-one-literal-as-default', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_enum_models_one_literal_as_default' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(pydantic.VERSION) < version.parse('1.9.0'), - reason='Require Pydantic version 1.9.0 or later ', -) -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@freeze_time('2019-07-26') -def test_main_openapi_enum_models_as_literal_all(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'enum_models.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--enum-field-as-literal', - 'all', - '--target-python-version', - '3.8', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_enum_models_all' / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(pydantic.VERSION) < version.parse('1.9.0'), - reason='Require Pydantic version 1.9.0 or later ', -) -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@freeze_time('2019-07-26') -def test_main_openapi_enum_models_as_literal_py37(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'enum_models.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--enum-field-as-literal', - 'all', - ] - ) - - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_enum_models_as_literal_py37' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_model_with_additional_properties(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' - ), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_root_model_with_additional_properties' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_model_with_additional_properties_use_generic_container_types(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' - ), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-generic-container-types', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_root_model_with_additional_properties_use_generic_container_types' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_model_with_additional_properties_use_standard_collections(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' - ), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-standard-collections', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_root_model_with_additional_properties_use_standard_collections' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_model_with_additional_properties_literal(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' - ), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--enum-field-as-literal', - 'all', - '--target-python-version', - '3.8', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_root_model_with_additional_properties_literal' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_multiple_files_ref(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'multiple_files_self_ref'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'multiple_files_self_ref' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_jsonschema_multiple_files_ref_test_json(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_SCHEMA_DATA_PATH / 'multiple_files_self_ref'): - return_code: Exit = main( - [ - '--input', - 'test.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'multiple_files_self_ref_single' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_simple_json_snake_case_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_DATA_PATH / 'simple.json'): - return_code: Exit = main( - [ - '--input', - 'simple.json', - '--output', - str(output_file), - '--input-file-type', - 'json', - '--snake-case-field', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'simple_json_snake_case_field' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_space_field_enum_snake_case_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_SCHEMA_DATA_PATH / 'space_field_enum.json'): - return_code: Exit = main( - [ - '--input', - 'space_field_enum.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--snake-case-field', - '--original-field-name-delimiter', - ' ', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_space_field_enum_snake_case_field' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_all_of_ref(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_SCHEMA_DATA_PATH / 'all_of_ref'): - return_code: Exit = main( - [ - '--input', - 'test.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--class-name', - 'Test', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'all_of_ref' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_all_of_with_object(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_SCHEMA_DATA_PATH): - return_code: Exit = main( - [ - '--input', - 'all_of_with_object.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'all_of_with_object' / 'output.py').read_text() - ) - - -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@freeze_time('2019-07-26') -def test_main_combined_array(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - with chdir(JSON_SCHEMA_DATA_PATH): - return_code: Exit = main( - [ - '--input', - 'combined_array.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'combined_array' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_openapi_all_of_required(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'allof_required.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_allof_required' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_openapi_nullable(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_openapi_nullable' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_nullable_strict_nullable(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--strict-nullable', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_nullable_strict_nullable' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_pattern', - ), - ( - 'pydantic_v2.BaseModel', - 'main_pattern_pydantic_v2', - ), - ( - 'msgspec.Struct', - 'main_pattern_msgspec', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_openapi_pattern(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'pattern.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--target-python', - '3.9', - '--output-model-type', - output_model, - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / expected_output / 'output.py' - ).read_text().replace('pattern.json', 'pattern.yaml') - - -@freeze_time('2019-07-26') -def test_main_jsonschema_pattern(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'pattern.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_pattern' / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'expected_output, args', - [ - ('main_pattern_with_lookaround_pydantic_v2', []), - ( - 'main_pattern_with_lookaround_pydantic_v2_field_constraints', - ['--field-constraints'], - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] < '22', - reason="Installed black doesn't support Python version 3.10", -) -def test_main_openapi_pattern_with_lookaround_pydantic_v2( - expected_output: str, args: List[str] -): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'pattern_lookaround.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--target-python', - '3.9', - '--output-model-type', - 'pydantic_v2.BaseModel', - *args, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_generate(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - input_ = (JSON_SCHEMA_DATA_PATH / 'person.json').relative_to(Path.cwd()) - assert not input_.is_absolute() - generate( - input_=input_, - input_file_type=InputFileType.JsonSchema, - output=output_file, - ) - - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_jsonschema' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_generate_non_pydantic_output(): - """ - See https://github.com/koxudaxi/datamodel-code-generator/issues/1452. - """ - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - input_ = (JSON_SCHEMA_DATA_PATH / 'simple_string.json').relative_to(Path.cwd()) - assert not input_.is_absolute() - generate( - input_=input_, - input_file_type=InputFileType.JsonSchema, - output=output_file, - output_model_type=DataModelType.DataclassesDataclass, - ) - - file = EXPECTED_MAIN_PATH / 'main_generate_non_pydantic_output' / 'output.py' - assert output_file.read_text() == file.read_text() - - -@freeze_time('2019-07-26') -def test_main_generate_from_directory(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - input_ = (JSON_SCHEMA_DATA_PATH / 'external_files_in_directory').relative_to( - Path.cwd() - ) - assert not input_.is_absolute() - assert input_.is_dir() - generate( - input_=input_, - input_file_type=InputFileType.JsonSchema, - output=output_path, - ) - - main_nested_directory = EXPECTED_MAIN_PATH / 'main_nested_directory' - - for path in main_nested_directory.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_nested_directory) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_generate_custom_class_name_generator(): - def custom_class_name_generator(title): - return f'Custom{title}' - - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - input_ = (JSON_SCHEMA_DATA_PATH / 'person.json').relative_to(Path.cwd()) - assert not input_.is_absolute() - generate( - input_=input_, - input_file_type=InputFileType.JsonSchema, - output=output_file, - custom_class_name_generator=custom_class_name_generator, - ) - - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_jsonschema' / 'output.py' - ).read_text().replace('Person', 'CustomPerson') - - -@freeze_time('2019-07-26') -def test_main_generate_custom_class_name_generator_modular( - tmpdir_factory: TempdirFactory, -): - output_directory = Path(tmpdir_factory.mktemp('output')) - - output_path = output_directory / 'model' - main_modular_custom_class_name_dir = ( - EXPECTED_MAIN_PATH / 'main_modular_custom_class_name' - ) - - def custom_class_name_generator(name): - return f'Custom{name[0].upper() + name[1:]}' - - with freeze_time(TIMESTAMP): - input_ = (OPEN_API_DATA_PATH / 'modular.yaml').relative_to(Path.cwd()) - assert not input_.is_absolute() - generate( - input_=input_, - input_file_type=InputFileType.OpenAPI, - output=output_path, - custom_class_name_generator=custom_class_name_generator, - ) - - for path in main_modular_custom_class_name_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_custom_class_name_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_generate_custom_class_name_generator_additional_properties( - tmpdir_factory: TempdirFactory, -): - output_directory = Path(tmpdir_factory.mktemp('output')) - - output_file = output_directory / 'models.py' - - def custom_class_name_generator(name): - return f'Custom{name[0].upper() + name[1:]}' - - input_ = ( - JSON_SCHEMA_DATA_PATH / 'root_model_with_additional_properties.json' - ).relative_to(Path.cwd()) - assert not input_.is_absolute() - generate( - input_=input_, - input_file_type=InputFileType.JsonSchema, - output=output_file, - custom_class_name_generator=custom_class_name_generator, - ) - - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_root_model_with_additional_properties_custom_class_name' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_http_jsonschema(mocker): - external_directory = JSON_SCHEMA_DATA_PATH / 'external_files_in_directory' - - def get_mock_response(path: str) -> mocker.Mock: - mock = mocker.Mock() - mock.text = (external_directory / path).read_text() - return mock - - httpx_get_mock = mocker.patch( - 'httpx.get', - side_effect=[ - get_mock_response('person.json'), - get_mock_response('definitions/relative/animal/pet/pet.json'), - get_mock_response('definitions/relative/animal/fur.json'), - get_mock_response('definitions/friends.json'), - get_mock_response('definitions/food.json'), - get_mock_response('definitions/machine/robot.json'), - get_mock_response('definitions/drink/coffee.json'), - get_mock_response('definitions/drink/tea.json'), - ], - ) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--url', - 'https://example.com/external_files_in_directory/person.json', - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_external_files_in_directory' / 'output.py' - ).read_text().replace( - '# filename: person.json', - '# filename: https://example.com/external_files_in_directory/person.json', - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/external_files_in_directory/person.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/relative/animal/pet/pet.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/relative/animal/fur.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/friends.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/food.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/machine/robot.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/drink/coffee.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://example.com/external_files_in_directory/definitions/drink/tea.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'headers_arguments,headers_requests,query_parameters_arguments,query_parameters_requests,http_ignore_tls', - [ - ( - ('Authorization: Basic dXNlcjpwYXNz',), - [('Authorization', 'Basic dXNlcjpwYXNz')], - ('key=value',), - [('key', 'value')], - False, - ), - ( - ('Authorization: Basic dXNlcjpwYXNz', 'X-API-key: abcefg'), - [('Authorization', 'Basic dXNlcjpwYXNz'), ('X-API-key', 'abcefg')], - ('key=value', 'newkey=newvalue'), - [('key', 'value'), ('newkey', 'newvalue')], - True, - ), - ], -) -def test_main_http_jsonschema_with_http_headers_and_http_query_parameters_and_ignore_tls( - mocker, - headers_arguments, - headers_requests, - query_parameters_arguments, - query_parameters_requests, - http_ignore_tls, -): - external_directory = JSON_SCHEMA_DATA_PATH / 'external_files_in_directory' - - def get_mock_response(path: str) -> mocker.Mock: - mock = mocker.Mock() - mock.text = (external_directory / path).read_text() - return mock - - httpx_get_mock = mocker.patch( - 'httpx.get', - side_effect=[ - get_mock_response('person.json'), - get_mock_response('definitions/relative/animal/pet/pet.json'), - get_mock_response('definitions/relative/animal/fur.json'), - get_mock_response('definitions/friends.json'), - get_mock_response('definitions/food.json'), - get_mock_response('definitions/machine/robot.json'), - get_mock_response('definitions/drink/coffee.json'), - get_mock_response('definitions/drink/tea.json'), - ], - ) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - args = [ - '--url', - 'https://example.com/external_files_in_directory/person.json', - '--http-headers', - *headers_arguments, - '--http-query-parameters', - *query_parameters_arguments, - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - if http_ignore_tls: - args.append('--http-ignore-tls') - - return_code: Exit = main(args) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_external_files_in_directory' / 'output.py' - ).read_text().replace( - '# filename: person.json', - '# filename: https://example.com/external_files_in_directory/person.json', - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/external_files_in_directory/person.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/relative/animal/pet/pet.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/relative/animal/fur.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/friends.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/food.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/machine/robot.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/drink/coffee.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - call( - 'https://example.com/external_files_in_directory/definitions/drink/tea.json', - headers=headers_requests, - verify=True if not http_ignore_tls else False, - follow_redirects=True, - params=query_parameters_requests, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_http_openapi(mocker): - def get_mock_response(path: str) -> mocker.Mock: - mock = mocker.Mock() - mock.text = (OPEN_API_DATA_PATH / path).read_text() - return mock - - httpx_get_mock = mocker.patch( - 'httpx.get', - side_effect=[ - get_mock_response('refs.yaml'), - get_mock_response('definitions.yaml'), - ], - ) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--url', - 'https://example.com/refs.yaml', - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_openapi_http_refs' / 'output.py').read_text() - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/refs.yaml', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - call( - 'https://teamdigitale.github.io/openapi/0.0.6/definitions.yaml', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_http_json(mocker): - def get_mock_response(path: str) -> mocker.Mock: - mock = mocker.Mock() - mock.text = (JSON_DATA_PATH / path).read_text() - return mock - - httpx_get_mock = mocker.patch( - 'httpx.get', - side_effect=[ - get_mock_response('pet.json'), - ], - ) - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--url', - 'https://example.com/pet.json', - '--output', - str(output_file), - '--input-file-type', - 'json', - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_json' / 'output.py' - ).read_text().replace( - '# filename: pet.json', - '# filename: https://example.com/pet.json', - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://example.com/pet.json', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_self_reference(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'self_reference.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_self_reference' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_disable_appending_item_suffix(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--field-constraints', - '--disable-appending-item-suffix', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_disable_appending_item_suffix' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_strict_types(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_strict_types' / 'output.py').read_text() - ) - - -@pytest.mark.skipif( - black.__version__.split('.')[0] >= '24', - reason="Installed black doesn't support the old style", -) -@freeze_time('2019-07-26') -def test_main_strict_types_all(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--strict-types', - 'str', - 'bytes', - 'int', - 'float', - 'bool', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_strict_types_all' / 'output.py').read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_strict_types_all_with_field_constraints(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'strict_types.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--strict-types', - 'str', - 'bytes', - 'int', - 'float', - 'bool', - '--field-constraints', - ] - ) - - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_strict_types_all_field_constraints' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_special_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_special_enum' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_special_enum_special_field_name_prefix(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--special-field-name-prefix', - 'special', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_special_enum_special_field_name_prefix' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_special_enum_special_field_name_prefix_keep_private(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--special-field-name-prefix', - '', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_special_enum_special_field_name_prefix_keep_private' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_special_model_remove_special_field_name_prefix(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_prefix_model.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--remove-special-field-name-prefix', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_special_model_remove_special_field_name_prefix' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_subclass_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'subclass_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-subclass-enum', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_subclass_enum' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_special_enum_empty_enum_field_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--empty-enum-field-name', - 'empty', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_special_enum_empty_enum_field_name' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_special_field_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'special_field_name.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_special_field_name' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_imports_correct(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'imports_correct'), - '--output', - str(output_path), - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'main_imports_correct' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_jsonschema_complex_one_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'complex_one_of.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_complex_one_of' / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_complex_any_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'complex_any_of.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_complex_any_of' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_combine_one_of_object(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'combine_one_of_object.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_combine_one_of_object' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_combine_any_of_object(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'combine_any_of_object.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_combine_any_of_object' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_jsonschema_field_include_all_keys(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--field-include-all-keys', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_jsonschema' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_jsonschema_field_extras_field_include_all_keys', - ), - ( - 'pydantic_v2.BaseModel', - 'main_jsonschema_field_extras_field_include_all_keys_v2', - ), - ], -) -def test_main_jsonschema_field_extras_field_include_all_keys( - output_model, expected_output -): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'extras.json'), - '--output', - str(output_file), - '--output-model', - output_model, - '--input-file-type', - 'jsonschema', - '--field-include-all-keys', - '--field-extra-keys-without-x-prefix', - 'x-repr', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_jsonschema_field_extras_field_extra_keys', - ), - ( - 'pydantic_v2.BaseModel', - 'main_jsonschema_field_extras_field_extra_keys_v2', - ), - ], -) -def test_main_jsonschema_field_extras_field_extra_keys(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'extras.json'), - '--output', - str(output_file), - '--output-model', - output_model, - '--input-file-type', - 'jsonschema', - '--field-extra-keys', - 'key2', - 'invalid-key-1', - '--field-extra-keys-without-x-prefix', - 'x-repr', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_jsonschema_field_extras', - ), - ( - 'pydantic_v2.BaseModel', - 'main_jsonschema_field_extras_v2', - ), - ], -) -def test_main_jsonschema_field_extras(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'extras.json'), - '--output', - str(output_file), - '--output-model', - output_model, - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.skipif( - not isort.__version__.startswith('4.'), - reason="isort 5.x don't sort pydantic modules", -) -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_jsonschema_custom_type_path', - ), - ( - 'pydantic_v2.BaseModel', - 'main_jsonschema_custom_type_path_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_jsonschema_custom_type_path(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'custom_type_path.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_custom_base_path(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'custom_base_path.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_custom_base_path' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_body_and_parameters(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--openapi-scopes', - 'paths', - 'schemas', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_body_and_parameters' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_body_and_parameters_remote_ref(mocker): - input_path = OPEN_API_DATA_PATH / 'body_and_parameters_remote_ref.yaml' - person_response = mocker.Mock() - person_response.text = input_path.read_text() - httpx_get_mock = mocker.patch('httpx.get', side_effect=[person_response]) - - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(input_path), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--openapi-scopes', - 'paths', - 'schemas', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_body_and_parameters_remote_ref' - / 'output.py' - ).read_text() - ) - httpx_get_mock.assert_has_calls( - [ - call( - 'https://schema.example', - headers=None, - verify=True, - follow_redirects=True, - params=None, - ), - ] - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_body_and_parameters_only_paths(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--openapi-scopes', - 'paths', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_body_and_parameters_only_paths' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_body_and_parameters_only_schemas(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--openapi-scopes', - 'schemas', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_body_and_parameters_only_schemas' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_content_in_parameters(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'content_in_parameters.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_content_in_parameters' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_oas_response_reference(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'oas_response_reference.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--openapi-scopes', - 'paths', - 'schemas', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_oas_response_reference' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_long_description(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'long_description.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_long_description' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_long_description_wrap_string_literal(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'long_description.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--wrap-string-literal', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_long_description_wrap_string_literal' - / 'output.py' - ).read_text() - ) - - -def test_version(capsys): - with pytest.raises(SystemExit) as e: - main(['--version']) - assert e.value.code == Exit.OK - captured = capsys.readouterr() - assert captured.out == '0.0.0\n' - assert captured.err == '' - - -@freeze_time('2019-07-26') -def test_main_openapi_json_pointer(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'json_pointer.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_json_pointer' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_jsonschema_pattern_properties(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'pattern_properties.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_pattern_properties' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_jsonschema_pattern_properties_field_constraints(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'pattern_properties.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--field-constraints', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_pattern_properties_field_constraints' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_jsonschema_titles(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'titles.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_jsonschema_titles' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_jsonschema_titles_use_title_as_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'titles.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-title-as-name', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_titles_use_title_as_name' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_jsonschema_without_titles_use_title_as_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'without_titles.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-title-as-name', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_without_titles_use_title_as_name' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ('pydantic.BaseModel', 'main_use_annotated_with_field_constraints'), - ( - 'pydantic_v2.BaseModel', - 'main_use_annotated_with_field_constraints_pydantic_v2', - ), - ], -) -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_use_annotated_with_field_constraints(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--field-constraints', - '--use-annotated', - '--target-python-version', - '3.9', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_use_annotated_with_field_constraints_py38(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--use-annotated', - '--target-python-version', - '3.8', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_use_annotated_with_field_constraints_py38' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_nested_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nested_enum.json'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_nested_enum' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_has_default_value(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'has_default_value.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'has_default_value' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_openapi_special_yaml_keywords(mocker): - mock_prance = mocker.patch('prance.BaseParser') - - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'special_yaml_keywords.yaml'), - '--output', - str(output_file), - '--validation', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_special_yaml_keywords' / 'output.py' - ).read_text() - ) - mock_prance.assert_called_once() - - -@freeze_time('2019-07-26') -def test_main_jsonschema_boolean_property(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'boolean_property.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_boolean_property' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_modular_default_enum_member( - tmpdir_factory: TempdirFactory, -) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = JSON_SCHEMA_DATA_PATH / 'modular_default_enum_member' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--set-default-enum-member', - ] - ) - main_modular_dir = ( - EXPECTED_MAIN_PATH / 'main_jsonschema_modular_default_enum_member' - ) - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - black.__version__.split('.')[0] < '22', - reason="Installed black doesn't support Python version 3.10", -) -@freeze_time('2019-07-26') -def test_main_use_union_operator(tmpdir_factory: TempdirFactory) -> None: - output_directory = Path(tmpdir_factory.mktemp('output')) - - output_path = output_directory / 'model' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'external_files_in_directory'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - '--use-union-operator', - ] - ) - assert return_code == Exit.OK - main_nested_directory = EXPECTED_MAIN_PATH / 'main_use_union_operator' - - for path in main_nested_directory.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_nested_directory) - ).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - black.__version__.split('.')[0] < '22', - reason="Installed black doesn't support Python version 3.10", -) -@freeze_time('2019-07-26') -def test_main_openapi_nullable_use_union_operator(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--use-union-operator', - '--strict-nullable', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_nullable_strict_nullable_use_union_operator' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_external_relative_ref(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'external_relative_ref' / 'model_b'), - '--output', - str(output_path), - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'external_relative_ref' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -@pytest.mark.parametrize('as_module', [True, False]) -def test_treat_dot_as_module(as_module): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - if as_module: - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'treat_dot_as_module'), - '--output', - str(output_path), - '--treat-dot-as-module', - ] - ) - else: - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'treat_dot_as_module'), - '--output', - str(output_path), - ] - ) - assert return_code == Exit.OK - path_extension = ( - 'treat_dot_as_module' if as_module else 'treat_dot_not_as_module' - ) - main_modular_dir = EXPECTED_MAIN_PATH / path_extension - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - if as_module: - assert str(path.relative_to(main_modular_dir)).count('.') == 1 - assert result == path.read_text() - - -@pytest.mark.benchmark -@freeze_time('2019-07-26') -def test_main_collapse_root_models(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'not_real_string.json'), - '--output', - str(output_file), - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_collapse_root_models' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_collapse_root_models_field_constraints(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'not_real_string.json'), - '--output', - str(output_file), - '--collapse-root-models', - '--field-constraints', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_collapse_root_models_field_constraints' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_collapse_root_models_with_references_to_flat_types(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'flat_type.jsonschema'), - '--output', - str(output_file), - '--collapse-root-models', - ] - ) - - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_collapse_root_models_with_references_to_flat_types' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_max_items_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'max_items_enum.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_max_items_enum' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_duplicate_name(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'duplicate_name'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'main_jsonschema_duplicate_name' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_jsonschema_items_boolean(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'items_boolean.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_items_boolean' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_array_in_additional_properites(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'array_in_additional_properties.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_array_in_additional_properties' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_nullable_object(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nullable_object.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_nullable_object' / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_openapi_const', - ), - ( - 'pydantic_v2.BaseModel', - 'main_openapi_const_pydantic_v2', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_openapi_const(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'const.json'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_openapi_const_field', - ), - ( - 'pydantic_v2.BaseModel', - 'main_openapi_const_field_pydantic_v2', - ), - ( - 'msgspec.Struct', - 'main_openapi_const_field_msgspec', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_openapi_const_field(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'const.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--output-model', - output_model, - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_complex_reference(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'complex_reference.json'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_complex_reference' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_reference_to_object_properties(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'reference_to_object_properties.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_reference_to_object_properties' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_reference_to_object_properties_collapse_root_models(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'reference_to_object_properties.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_reference_to_object_properties_collapse_root_models' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_override_required_all_of_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'override_required_all_of.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_override_required_all_of' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_object_has_one_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'object_has_one_of.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_object_has_one_of' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_json_pointer_array(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'json_pointer_array.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_json_pointer_array' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_use_default_kwarg(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--use-default-kwarg', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_use_default_kwarg' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_json_snake_case_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'snake_case.json'), - '--output', - str(output_file), - '--input-file-type', - 'json', - '--snake-case-field', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_json_snake_case_field' / 'output.py' - ).read_text() - ) - - -@pytest.mark.filterwarnings('error') -def test_main_disable_warnings_config(capsys: CaptureFixture): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-union-operator', - '--target-python-version', - '3.6', - '--disable-warnings', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.OK - assert captured.err == '' - - -@pytest.mark.filterwarnings('error') -def test_main_disable_warnings(capsys: CaptureFixture): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'all_of_with_object.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--disable-warnings', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.OK - assert captured.err == '' - - -@pytest.mark.parametrize( - 'input,output', - [ - ( - 'discriminator.yaml', - 'main_openapi_discriminator', - ), - ( - 'discriminator_without_mapping.yaml', - 'main_openapi_discriminator_without_mapping', - ), - ], -) -@freeze_time('2019-07-26') -def test_main_openapi_discriminator(input, output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / input), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / output / 'output.py').read_text() - ) - - -@freeze_time('2023-07-27') -@pytest.mark.parametrize( - 'kind,option, expected', - [ - ( - 'anyOf', - '--collapse-root-models', - 'main_openapi_discriminator_in_array_collapse_root_models', - ), - ( - 'oneOf', - '--collapse-root-models', - 'main_openapi_discriminator_in_array_collapse_root_models', - ), - ('anyOf', None, 'main_openapi_discriminator_in_array'), - ('oneOf', None, 'main_openapi_discriminator_in_array'), - ], -) -def test_main_openapi_discriminator_in_array(kind, option, expected): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - input_file = f'discriminator_in_array_{kind.lower()}.yaml' - return_code: Exit = main( - [ - a - for a in [ - '--input', - str(OPEN_API_DATA_PATH / input_file), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - option, - ] - if a - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / expected / 'output.py' - ).read_text().replace('discriminator_in_array.yaml', input_file) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_pattern_properties_by_reference(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'pattern_properties_by_reference.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_pattern_properties_by_reference' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_openapi_default_object', - ), - ( - 'pydantic_v2.BaseModel', - 'main_openapi_pydantic_v2_default_object', - ), - ( - 'msgspec.Struct', - 'main_openapi_msgspec_default_object', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_openapi_default_object(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'default_object.yaml'), - '--output', - str(output_dir), - '--output-model', - output_model, - '--input-file-type', - 'openapi', - '--target-python-version', - '3.9', - ] - ) - assert return_code == Exit.OK - - main_modular_dir = EXPECTED_MAIN_PATH / expected_output - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text(), path - - -@freeze_time('2019-07-26') -def test_main_dataclass(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_dataclass' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_dataclass_base_class(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - '--base-class', - 'custom_base.Base', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_dataclass_base_class' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_dataclass_field(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'user.json'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_dataclass_field' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_jsonschema_enum_root_literal(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'enum_in_root' / 'enum_in_root.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--use-schema-description', - '--use-title-as-name', - '--field-constraints', - '--target-python-version', - '3.9', - '--allow-population-by-field-name', - '--strip-default-none', - '--use-default', - '--enum-field-as-literal', - 'all', - '--snake-case-field', - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_jsonschema_root_in_enum' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_reference_same_hierarchy_directory(): - with TemporaryDirectory() as output_dir: - with chdir(JSON_SCHEMA_DATA_PATH / 'reference_same_hierarchy_directory'): - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - './public/entities.yaml', - '--output', - str(output_file), - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_reference_same_hierarchy_directory' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_multiple_required_any_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'multiple_required_any_of.yaml'), - '--output', - str(output_file), - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_multiple_required_any_of' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_nullable_any_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nullable_any_of.json'), - '--output', - str(output_file), - '--field-constraints', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_nullable_any_of' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_nullable_any_of_use_union_operator(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nullable_any_of.json'), - '--output', - str(output_file), - '--field-constraints', - '--use-union-operator', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_nullable_any_of_use_union_operator' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_nested_all_of(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'nested_all_of.json'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_nested_all_of' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_max_min_openapi(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'max_min_number.yaml'), - '--output', - str(output_file), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'max_min_number' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_use_operation_id_as_name(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--use-operation-id-as-name', - '--openapi-scopes', - 'paths', - 'schemas', - 'parameters', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_use_operation_id_as_name' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_use_operation_id_as_name_not_found_operation_id(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'body_and_parameters.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--use-operation-id-as-name', - '--openapi-scopes', - 'paths', - 'schemas', - 'parameters', - ] - ) - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err - == 'All operations must have an operationId when --use_operation_id_as_name is set.' - 'The following path was missing an operationId: pets\n' - ) - - -@freeze_time('2019-07-26') -def test_main_unsorted_optional_fields(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'unsorted_optional_fields.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'unsorted_optional_fields' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_all_of_any_of(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'all_of_any_of'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - all_of_any_of_dir = EXPECTED_MAIN_PATH / 'main_all_of_any_of' - for path in all_of_any_of_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(all_of_any_of_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_all_of_one_of(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'all_of_one_of'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - all_of_any_of_dir = EXPECTED_MAIN_PATH / 'main_all_of_one_of' - for path in all_of_any_of_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(all_of_any_of_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_null(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'null.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_null' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_typed_dict(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_typed_dict' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_typed_dict_py_38(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.8', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_typed_dict_py_38' / 'output.py').read_text() - ) - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_typed_dict_space_and_special_characters(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_DATA_PATH / 'space_and_special_characters.json'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_typed_dict_space_and_special_characters' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -def test_main_modular_typed_dict(tmpdir_factory: TempdirFactory) -> None: - """Test main function on modular file.""" - - output_directory = Path(tmpdir_factory.mktemp('output')) - - input_filename = OPEN_API_DATA_PATH / 'modular.yaml' - output_path = output_directory / 'model' - - with freeze_time(TIMESTAMP): - main( - [ - '--input', - str(input_filename), - '--output', - str(output_path), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - ] - ) - main_modular_dir = EXPECTED_MAIN_PATH / 'main_modular_typed_dict' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath(path.relative_to(main_modular_dir)).read_text() - assert result == path.read_text() - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_typed_dict_special_field_name_with_inheritance_model(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH - / 'special_field_name_with_inheritance_model.json' - ), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_typed_dict_special_field_name_with_inheritance_model' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_typed_dict_not_required_nullable(): - """Test main function writing to TypedDict, with combos of Optional/NotRequired.""" - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'not_required_nullable.json'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_typed_dict_not_required_nullable' - / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_typed_dict_nullable(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_typed_dict_nullable' / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - version.parse(black.__version__) < version.parse('23.3.0'), - reason='Require Black version 23.3.0 or later ', -) -@freeze_time('2019-07-26') -def test_main_typed_dict_nullable_strict_nullable(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'nullable.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'typing.TypedDict', - '--target-python-version', - '3.11', - '--strict-nullable', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_typed_dict_nullable_strict_nullable' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_custom_file_header_path(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--custom-file-header-path', - str(DATA_PATH / 'custom_file_header.txt'), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_custom_file_header' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_custom_file_header_duplicate_options(capsys): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--custom-file-header-path', - str(DATA_PATH / 'custom_file_header.txt'), - '--custom-file-header', - 'abc', - ] - ) - - captured = capsys.readouterr() - assert return_code == Exit.ERROR - assert ( - captured.err - == '`--custom_file_header_path` can not be used with `--custom_file_header`.\n' - ) - - -@freeze_time('2019-07-26') -def test_main_pydantic_v2(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_pydantic_v2' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_openapi_custom_id_pydantic_v2(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'custom_id.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_custom_id_pydantic_v2' / 'output.py' - ).read_text() - ) - - -@pytest.mark.skipif( - not isort.__version__.startswith('4.'), - reason="isort 5.x don't sort pydantic modules", -) -@freeze_time('2019-07-26') -def test_main_openapi_custom_id_pydantic_v2_custom_base(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'custom_id.yaml'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - '--base-class', - 'custom_base.Base', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_custom_id_pydantic_v2_custom_base' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_discriminator_literals(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'discriminator_literals.json'), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_discriminator_literals' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_external_discriminator(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str( - JSON_SCHEMA_DATA_PATH - / 'discriminator_with_external_reference' - / 'inner_folder' - / 'schema.json' - ), - '--output', - str(output_file), - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'discriminator_with_external_reference' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_external_discriminator_folder(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'discriminator_with_external_reference'), - '--output', - str(output_path), - ] - ) - assert return_code == Exit.OK - main_modular_dir = ( - EXPECTED_MAIN_PATH / 'discriminator_with_external_references_folder' - ) - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_openapi_all_of_with_relative_ref(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'all_of_with_relative_ref' / 'openapi.yaml'), - '--output', - str(output_file), - '--input-file-type', - 'openapi', - '--output-model-type', - 'pydantic_v2.BaseModel', - '--keep-model-order', - '--collapse-root-models', - '--field-constraints', - '--use-title-as-name', - '--field-include-all-keys', - '--use-field-description', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_all_of_with_relative_ref' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_msgspec_struct(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api.yaml'), - '--output', - str(output_file), - # min msgspec python version is 3.8 - '--target-python-version', - '3.8', - '--output-model-type', - 'msgspec.Struct', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_msgspec_struct' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_msgspec_struct_snake_case(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_ordered_required_fields.yaml'), - '--output', - str(output_file), - # min msgspec python version is 3.8 - '--target-python-version', - '3.8', - '--snake-case-field', - '--output-model-type', - 'msgspec.Struct', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_msgspec_struct_snake_case' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_msgspec_use_annotated_with_field_constraints(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'api_constrained.yaml'), - '--output', - str(output_file), - '--field-constraints', - '--target-python-version', - '3.9', - '--output-model-type', - 'msgspec.Struct', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_use_annotated_with_msgspec_meta_constraints' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_duplicate_field_constraints(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'duplicate_field_constraints'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - '--collapse-root-models', - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / 'duplicate_field_constraints' - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@pytest.mark.parametrize( - 'collapse_root_models,python_version,expected_output', - [ - ( - '--collapse-root-models', - '3.8', - 'duplicate_field_constraints_msgspec_py38_collapse_root_models', - ), - ( - None, - '3.9', - 'duplicate_field_constraints_msgspec', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_duplicate_field_constraints_msgspec( - collapse_root_models, python_version, expected_output -): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - a - for a in [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'duplicate_field_constraints'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - '--output-model-type', - 'msgspec.Struct', - '--target-python-version', - python_version, - collapse_root_models, - ] - if a - ] - ) - assert return_code == Exit.OK - main_modular_dir = EXPECTED_MAIN_PATH / expected_output - for path in main_modular_dir.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(main_modular_dir) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_main_dataclass_field_defs(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'user_defs.json'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - ] - ) - assert return_code == Exit.OK - assert output_file.read_text() == ( - EXPECTED_MAIN_PATH / 'main_dataclass_field' / 'output.py' - ).read_text().replace('filename: user.json', 'filename: user_defs.json') - - -@freeze_time('2019-07-26') -def test_main_dataclass_default(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'user_default.json'), - '--output', - str(output_file), - '--output-model-type', - 'dataclasses.dataclass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_dataclass_field_default' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_all_of_ref_self(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'all_of_ref_self.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_all_of_ref_self' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_array_field_constraints(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'array_field_constraints.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--target-python-version', - '3.9', - '--field-constraints', - '--collapse-root-models', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_array_field_constraints' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_all_of_use_default(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'all_of_default.json'), - '--output', - str(output_file), - '--use-default', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_all_of_use_default' / 'output.py' - ).read_text() - ) - - -@pytest.mark.parametrize( - 'output_model,expected_output', - [ - ( - 'pydantic.BaseModel', - 'main_graphql_simple_star_wars', - ), - ( - 'dataclasses.dataclass', - 'main_graphql_simple_star_wars_dataclass', - ), - ], -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_simple_star_wars(output_model, expected_output): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'simple-star-wars.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--output-model', - output_model, - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / expected_output / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_different_types_of_fields(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'different-types-of-fields.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_graphql_different_types_of_fields' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_custom_scalar_types(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'custom-scalar-types.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--extra-template-data', - str(GRAPHQL_DATA_PATH / 'custom-scalar-types.json'), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_graphql_custom_scalar_types' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_field_aliases(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'field-aliases.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--aliases', - str(GRAPHQL_DATA_PATH / 'field-aliases.json'), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_graphql_field_aliases' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_enums(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'enums.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_graphql_enums' / 'output.py').read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_union(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'union.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == (EXPECTED_MAIN_PATH / 'main_graphql_union' / 'output.py').read_text() - ) - - -@pytest.mark.skipif( - not isort.__version__.startswith('4.'), - reason='See https://github.com/PyCQA/isort/issues/1600 for example', -) -@freeze_time('2019-07-26') -def test_main_graphql_additional_imports_isort_4(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'additional-imports.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--extra-template-data', - str(GRAPHQL_DATA_PATH / 'additional-imports-types.json'), - '--additional-imports', - 'datetime.datetime,datetime.date,mymodule.myclass.MyCustomPythonClass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_graphql_additional_imports' - / 'output_isort4.py' - ).read_text() - ) - - -@pytest.mark.skipif( - not isort.__version__.startswith('5.'), - reason='See https://github.com/PyCQA/isort/issues/1600 for example', -) -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_additional_imports_isort_5(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'additional-imports.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--extra-template-data', - str(GRAPHQL_DATA_PATH / 'additional-imports-types.json'), - '--additional-imports', - 'datetime.datetime,datetime.date,mymodule.myclass.MyCustomPythonClass', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_graphql_additional_imports' - / 'output_isort5.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_graphql_custom_formatters(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(GRAPHQL_DATA_PATH / 'custom-scalar-types.graphql'), - '--output', - str(output_file), - '--input-file-type', - 'graphql', - '--custom-formatters', - 'tests.data.python.custom_formatters.add_comment', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_graphql_custom_formatters' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_openapi_discriminator_enum(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'discriminator_enum.yaml'), - '--output', - str(output_file), - '--target-python-version', - '3.10', - '--output-model-type', - 'pydantic_v2.BaseModel', - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH / 'main_openapi_discriminator_enum' / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -@pytest.mark.skipif( - black.__version__.split('.')[0] == '19', - reason="Installed black doesn't support the old style", -) -def test_main_openapi_discriminator_enum_duplicate(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(OPEN_API_DATA_PATH / 'discriminator_enum_duplicate.yaml'), - '--output', - str(output_file), - '--target-python-version', - '3.10', - '--output-model-type', - 'pydantic_v2.BaseModel', - '--input-file-type', - 'openapi', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_openapi_discriminator_enum_duplicate' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_root_one_of(): - with TemporaryDirectory() as output_dir: - output_path: Path = Path(output_dir) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'root_one_of'), - '--output', - str(output_path), - '--input-file-type', - 'jsonschema', - ] - ) - assert return_code == Exit.OK - expected_directory = EXPECTED_MAIN_PATH / 'main_root_one_of' - for path in expected_directory.rglob('*.py'): - result = output_path.joinpath( - path.relative_to(expected_directory) - ).read_text() - assert result == path.read_text() - - -@freeze_time('2019-07-26') -def test_one_of_with_sub_schema_array_item(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'one_of_with_sub_schema_array_item.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--output-model-type', - 'pydantic_v2.BaseModel', - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_one_of_with_sub_schema_array_item' - / 'output.py' - ).read_text() - ) - - -@freeze_time('2019-07-26') -def test_main_jsonschema_with_custom_formatters(): - with TemporaryDirectory() as output_dir: - output_file: Path = Path(output_dir) / 'output.py' - formatter_config = { - 'license_file': str( - Path(__file__).parent - / 'data/python/custom_formatters/license_example.txt' - ) - } - formatter_config_path = Path(output_dir, 'formatter_config') - with formatter_config_path.open('w') as f: - json.dump(formatter_config, f) - return_code: Exit = main( - [ - '--input', - str(JSON_SCHEMA_DATA_PATH / 'person.json'), - '--output', - str(output_file), - '--input-file-type', - 'jsonschema', - '--custom-formatters', - 'tests.data.python.custom_formatters.add_license', - '--custom-formatters-kwargs', - str(formatter_config_path), - ] - ) - assert return_code == Exit.OK - assert ( - output_file.read_text() - == ( - EXPECTED_MAIN_PATH - / 'main_jsonschema_with_custom_formatters' - / 'output.py' - ).read_text() - )