diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c421c2b..a555ccd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,6 @@ on: jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: false @@ -16,20 +15,20 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[test] - - name: Pytest - run: | - pytest - - name: Linters - run: | - pylint ariadne_graphql_modules tests - mypy ariadne_graphql_modules --ignore-missing-imports - black --check . + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[test] + - name: Pytest + run: | + pytest + - name: Linters + run: | + pylint ariadne_graphql_modules + mypy ariadne_graphql_modules --ignore-missing-imports + black --check . diff --git a/ariadne_graphql_modules/__init__.py b/ariadne_graphql_modules/__init__.py index 789effe..c80762d 100644 --- a/ariadne_graphql_modules/__init__.py +++ b/ariadne_graphql_modules/__init__.py @@ -1,38 +1,58 @@ -from ariadne import gql - -from .bases import BaseType, BindableType, DeferredType, DefinitionType -from .collection_type import CollectionType -from .convert_case import convert_case -from .directive_type import DirectiveType -from .enum_type import EnumType +from .base import GraphQLMetadata, GraphQLModel, GraphQLType +from .convert_name import ( + convert_graphql_name_to_python, + convert_python_name_to_graphql, +) +from .deferredtype import deferred +from .description import get_description_node +from .enum_type import ( + GraphQLEnum, + GraphQLEnumModel, + create_graphql_enum_model, + graphql_enum, +) from .executable_schema import make_executable_schema -from .input_type import InputType -from .interface_type import InterfaceType -from .mutation_type import MutationType -from .object_type import ObjectType -from .scalar_type import ScalarType -from .subscription_type import SubscriptionType -from .union_type import UnionType -from .utils import create_alias_resolver, parse_definition +from .idtype import GraphQLID +from .input_type import GraphQLInput, GraphQLInputModel +from .object_type import GraphQLObject, GraphQLObjectModel, object_field +from .roots import ROOTS_NAMES, merge_root_nodes +from .scalar_type import GraphQLScalar, GraphQLScalarModel +from .sort import sort_schema_document +from .union_type import GraphQLUnion, GraphQLUnionModel +from .value import get_value_from_node, get_value_node +from .interface_type import GraphQLInterface, GraphQLInterfaceModel +from .subscription_type import GraphQLSubscription, GraphQLSubscriptionModel __all__ = [ - "BaseType", - "BindableType", - "CollectionType", - "DeferredType", - "DefinitionType", - "DirectiveType", - "EnumType", - "InputType", - "InterfaceType", - "MutationType", - "ObjectType", - "ScalarType", - "SubscriptionType", - "UnionType", - "convert_case", - "create_alias_resolver", - "gql", + "GraphQLEnum", + "GraphQLEnumModel", + "GraphQLID", + "GraphQLInput", + "GraphQLInputModel", + "GraphQLInterface", + "GraphQLInterfaceModel", + "GraphQLSubscription", + "GraphQLSubscriptionModel", + "GraphQLMetadata", + "GraphQLModel", + "GraphQLObject", + "GraphQLObjectModel", + "GraphQLScalar", + "GraphQLScalarModel", + "GraphQLType", + "GraphQLUnion", + "GraphQLUnionModel", + "ROOTS_NAMES", + "convert_graphql_name_to_python", + "convert_python_name_to_graphql", + "create_graphql_enum_model", + "deferred", + "get_description_node", + "get_value_from_node", + "get_value_node", + "graphql_enum", "make_executable_schema", - "parse_definition", + "merge_root_nodes", + "object_field", + "sort_schema_document", ] diff --git a/ariadne_graphql_modules/next/base.py b/ariadne_graphql_modules/base.py similarity index 97% rename from ariadne_graphql_modules/next/base.py rename to ariadne_graphql_modules/base.py index fcf7991..aa57f2a 100644 --- a/ariadne_graphql_modules/next/base.py +++ b/ariadne_graphql_modules/base.py @@ -84,7 +84,7 @@ def get_graphql_model( if hasattr(graphql_type, "__get_graphql_model__"): self.models[graphql_type] = graphql_type.__get_graphql_model__(self) elif issubclass(graphql_type, Enum): - from .graphql_enum_type import ( # pylint: disable=R0401,C0415 + from .enum_type import ( # pylint: disable=R0401,C0415 create_graphql_enum_model, ) diff --git a/ariadne_graphql_modules/base_object_type/__init__.py b/ariadne_graphql_modules/base_object_type/__init__.py new file mode 100644 index 0000000..6884aa9 --- /dev/null +++ b/ariadne_graphql_modules/base_object_type/__init__.py @@ -0,0 +1,15 @@ +from .graphql_type import GraphQLBaseObject +from .graphql_field import GraphQLFieldData, GraphQLObjectData +from .validators import ( + validate_object_type_with_schema, + validate_object_type_without_schema, +) + + +__all__ = [ + "GraphQLBaseObject", + "GraphQLObjectData", + "GraphQLFieldData", + "validate_object_type_with_schema", + "validate_object_type_without_schema", +] diff --git a/ariadne_graphql_modules/next/graphql_object/object_field.py b/ariadne_graphql_modules/base_object_type/graphql_field.py similarity index 78% rename from ariadne_graphql_modules/next/graphql_object/object_field.py rename to ariadne_graphql_modules/base_object_type/graphql_field.py index 15b5491..a7ced74 100644 --- a/ariadne_graphql_modules/next/graphql_object/object_field.py +++ b/ariadne_graphql_modules/base_object_type/graphql_field.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass -from typing import Any, Dict, Optional +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional from ariadne.types import Resolver, Subscriber @@ -12,6 +12,47 @@ class GraphQLObjectFieldArg: default_value: Optional[Any] = None +@dataclass(frozen=True) +class GraphQLObjectData: + fields: Dict[str, "GraphQLObjectField"] + interfaces: List[str] + + +@dataclass(frozen=True) +class GraphQLObjectResolver: + resolver: Resolver + field: str + description: Optional[str] = None + args: Optional[Dict[str, GraphQLObjectFieldArg]] = None + field_type: Optional[Any] = None + + +@dataclass(frozen=True) +class GraphQLObjectSource: + subscriber: Subscriber + field: str + description: Optional[str] = None + args: Optional[Dict[str, GraphQLObjectFieldArg]] = None + field_type: Optional[Any] = None + + +@dataclass +class GraphQLFieldData: + fields_types: Dict[str, str] = field(default_factory=dict) + fields_names: Dict[str, str] = field(default_factory=dict) + fields_descriptions: Dict[str, str] = field(default_factory=dict) + fields_args: Dict[str, Dict[str, GraphQLObjectFieldArg]] = field( + default_factory=dict + ) + fields_resolvers: Dict[str, Resolver] = field(default_factory=dict) + fields_subscribers: Dict[str, Subscriber] = field(default_factory=dict) + fields_defaults: Dict[str, Any] = field(default_factory=dict) + fields_order: List[str] = field(default_factory=list) + type_hints: Dict[str, Any] = field(default_factory=dict) + aliases: Dict[str, str] = field(default_factory=dict) + aliases_targets: List[str] = field(default_factory=list) + + class GraphQLObjectField: def __init__( self, @@ -40,24 +81,6 @@ def __call__(self, resolver: Resolver): return self -@dataclass(frozen=True) -class GraphQLObjectResolver: - resolver: Resolver - field: str - description: Optional[str] = None - args: Optional[Dict[str, GraphQLObjectFieldArg]] = None - field_type: Optional[Any] = None - - -@dataclass(frozen=True) -class GraphQLObjectSource: - subscriber: Subscriber - field: str - description: Optional[str] = None - args: Optional[Dict[str, GraphQLObjectFieldArg]] = None - field_type: Optional[Any] = None - - def object_field( resolver: Optional[Resolver] = None, *, diff --git a/ariadne_graphql_modules/next/graphql_object/object_type.py b/ariadne_graphql_modules/base_object_type/graphql_type.py similarity index 57% rename from ariadne_graphql_modules/next/graphql_object/object_type.py rename to ariadne_graphql_modules/base_object_type/graphql_type.py index 80f8e67..6dc9b9d 100644 --- a/ariadne_graphql_modules/next/graphql_object/object_type.py +++ b/ariadne_graphql_modules/base_object_type/graphql_type.py @@ -7,22 +7,24 @@ Iterable, List, Optional, + Tuple, Type, Union, - cast, ) -from ariadne.types import Resolver, Subscriber +from ariadne.types import Resolver from graphql import ( FieldDefinitionNode, InputValueDefinitionNode, - NameNode, - NamedTypeNode, ObjectTypeDefinitionNode, StringValueNode, ) -from .object_field import ( +from ..types import GraphQLClassType + +from .graphql_field import ( + GraphQLFieldData, + GraphQLObjectData, GraphQLObjectField, GraphQLObjectFieldArg, GraphQLObjectResolver, @@ -30,7 +32,6 @@ object_field, object_resolver, ) -from .object_model import GraphQLObjectModel from .utils import ( get_field_args_from_resolver, get_field_args_from_subscriber, @@ -39,7 +40,6 @@ update_field_args_options, ) -from ...utils import parse_definition from ..base import GraphQLMetadata, GraphQLModel, GraphQLType from ..convert_name import convert_python_name_to_graphql from ..description import get_description_node @@ -51,26 +51,21 @@ from ..value import get_value_node -@dataclass(frozen=True) -class GraphQLObjectData: - fields: Dict[str, "GraphQLObjectField"] - interfaces: List[str] - - -class GraphQLObject(GraphQLType): +class GraphQLBaseObject(GraphQLType): __kwargs__: Dict[str, Any] __abstract__: bool = True __schema__: Optional[str] __description__: Optional[str] __aliases__: Optional[Dict[str, str]] __requires__: Optional[Iterable[Union[Type[GraphQLType], Type[Enum]]]] - __implements__: Optional[Iterable[Type[GraphQLType]]] + __graphql_type__ = GraphQLClassType.BASE def __init__(self, **kwargs: Any): default_values: Dict[str, Any] = {} - for interface in getattr(self, "__implements__", []): - if hasattr(interface, "__kwargs__"): - default_values.update(interface.__kwargs__) + + for inherited_obj in self._collect_inherited_objects(): + if hasattr(inherited_obj, "__kwargs__"): + default_values.update(inherited_obj.__kwargs__) default_values.update(self.__kwargs__) @@ -112,16 +107,22 @@ def __get_graphql_model__(cls, metadata: GraphQLMetadata) -> "GraphQLModel": @classmethod def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": - definition = cast( - ObjectTypeDefinitionNode, - parse_definition(ObjectTypeDefinitionNode, cls.__schema__), - ) + raise NotImplementedError() + + @classmethod + def __get_graphql_model_without_schema__( + cls, metadata: GraphQLMetadata, name: str + ) -> "GraphQLModel": + raise NotImplementedError() + @classmethod + def _create_fields_and_resolvers_with_schema( + cls, definition_fields: Tuple["FieldDefinitionNode", ...] + ) -> Tuple[tuple[FieldDefinitionNode, ...], Dict[str, Resolver]]: descriptions: Dict[str, StringValueNode] = {} args_descriptions: Dict[str, Dict[str, StringValueNode]] = {} args_defaults: Dict[str, Dict[str, Any]] = {} resolvers: Dict[str, Resolver] = {} - out_names: Dict[str, Dict[str, str]] = {} for attr_name in dir(cls): cls_attr = getattr(cls, attr_name) @@ -133,26 +134,24 @@ def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": field_args = get_field_args_from_resolver(cls_attr.resolver) if field_args: - args_descriptions[cls_attr.field] = {} - args_defaults[cls_attr.field] = {} + args_descriptions[cls_attr.field], args_defaults[cls_attr.field] = ( + {}, + {}, + ) final_args = update_field_args_options(field_args, cls_attr.args) - for arg_name, arg_options in final_args.items(): - arg_description = get_description_node(arg_options.description) - if arg_description: - args_descriptions[cls_attr.field][ - arg_name - ] = arg_description - - arg_default = arg_options.default_value - if arg_default is not None: + description_node = get_description_node(cls_attr.description) + if description_node: + descriptions[cls_attr.field] = description_node + + if arg_options.default_value is not None: args_defaults[cls_attr.field][arg_name] = get_value_node( - arg_default + arg_options.default_value ) fields: List[FieldDefinitionNode] = [] - for field in definition.fields: + for field in definition_fields: field_args_descriptions = args_descriptions.get(field.name.value, {}) field_args_defaults = args_defaults.get(field.name.value, {}) @@ -185,30 +184,21 @@ def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": ) ) - return GraphQLObjectModel( - name=definition.name.value, - ast_type=ObjectTypeDefinitionNode, - ast=ObjectTypeDefinitionNode( - name=NameNode(value=definition.name.value), - fields=tuple(fields), - interfaces=definition.interfaces, - ), - resolvers=resolvers, - aliases=getattr(cls, "__aliases__", {}), - out_names=out_names, - ) + return tuple(fields), resolvers @classmethod - def __get_graphql_model_without_schema__( - cls, metadata: GraphQLMetadata, name: str - ) -> "GraphQLModel": - type_data = get_graphql_object_data(metadata, cls) - type_aliases = getattr(cls, "__aliases__", {}) - - fields_ast: List[FieldDefinitionNode] = [] - resolvers: Dict[str, Resolver] = {} - aliases: Dict[str, str] = {} - out_names: Dict[str, Dict[str, str]] = {} + def _process_graphql_fields( + cls, metadata: GraphQLMetadata, type_data, type_aliases + ) -> Tuple[ + List[FieldDefinitionNode], + Dict[str, Resolver], + Dict[str, str], + Dict[str, Dict[str, str]], + ]: + fields_ast = [] + resolvers = {} + aliases = {} + out_names = {} for attr_name, field in type_data.fields.items(): fields_ast.append(get_field_node_from_obj_field(cls, metadata, field)) @@ -224,25 +214,7 @@ def __get_graphql_model_without_schema__( if field.args and field.name: out_names[field.name] = get_field_args_out_names(field.args) - interfaces_ast: List[NamedTypeNode] = [] - for interface_name in type_data.interfaces: - interfaces_ast.append(NamedTypeNode(name=NameNode(value=interface_name))) - - return GraphQLObjectModel( - name=name, - ast_type=ObjectTypeDefinitionNode, - ast=ObjectTypeDefinitionNode( - name=NameNode(value=name), - description=get_description_node( - getattr(cls, "__description__", None), - ), - fields=tuple(fields_ast), - interfaces=tuple(interfaces_ast), - ), - resolvers=resolvers, - aliases=aliases, - out_names=out_names, - ) + return fields_ast, resolvers, aliases, out_names @classmethod def __get_graphql_types__( @@ -268,7 +240,7 @@ def __get_graphql_types_without_schema__( cls, metadata: "GraphQLMetadata" ) -> Iterable[Union[Type["GraphQLType"], Type[Enum]]]: types: List[Union[Type["GraphQLType"], Type[Enum]]] = [cls] - type_data = get_graphql_object_data(metadata, cls) + type_data = cls.get_graphql_object_data(metadata) for field in type_data.fields.values(): field_type = get_graphql_type(field.field_type) @@ -333,130 +305,135 @@ def argument( default_value=default_value, ) + @classmethod + def get_graphql_object_data( + cls, + metadata: GraphQLMetadata, + ) -> GraphQLObjectData: + try: + return metadata.get_data(cls) + except KeyError as exc: + if getattr(cls, "__schema__", None): + raise NotImplementedError( + "'get_graphql_object_data' is not supported for " + "objects with '__schema__'." + ) from exc + object_data = cls.create_graphql_object_data_without_schema() + + metadata.set_data(cls, object_data) + return object_data -def get_graphql_object_data( - metadata: GraphQLMetadata, cls: Type[GraphQLObject] -) -> GraphQLObjectData: - try: - return metadata.get_data(cls) - except KeyError as exc: - if getattr(cls, "__schema__", None): - raise NotImplementedError( - "'get_graphql_object_data' is not supported for " - "objects with '__schema__'." - ) from exc - object_data = create_graphql_object_data_without_schema(cls) - - metadata.set_data(cls, object_data) - return object_data - - -def create_graphql_object_data_without_schema( - cls: Type["GraphQLObject"], -) -> GraphQLObjectData: - fields_types: Dict[str, str] = {} - fields_names: Dict[str, str] = {} - fields_descriptions: Dict[str, str] = {} - fields_args: Dict[str, Dict[str, GraphQLObjectFieldArg]] = {} - fields_resolvers: Dict[str, Resolver] = {} - fields_subscribers: Dict[str, Subscriber] = {} - fields_defaults: Dict[str, Any] = {} - fields_order: List[str] = [] - - interfaces = getattr(cls, "__implements__", []) - interfaces_names: List[str] = [interface.__name__ for interface in interfaces] - type_hints: dict[str, Any] = {} - aliases: Dict[str, str] = {} - - for interface in reversed(interfaces): - type_hints.update(interface.__annotations__) - aliases.update(getattr(interface, "__aliases__", {})) - - type_hints.update(cls.__annotations__) - aliases.update(getattr(cls, "__aliases__", None) or {}) - aliases_targets: List[str] = list(aliases.values()) - - for attr_name, attr_type in type_hints.items(): - if attr_name.startswith("__"): - continue - - if attr_name in aliases_targets: - # Alias target is not included in schema - # unless its explicit field - cls_attr = getattr(cls, attr_name, None) - if not isinstance(cls_attr, GraphQLObjectField): + @classmethod + def create_graphql_object_data_without_schema(cls) -> GraphQLObjectData: + raise NotImplementedError() + + @staticmethod + def _build_fields(fields_data: GraphQLFieldData) -> Dict[str, "GraphQLObjectField"]: + fields = {} + for field_name in fields_data.fields_order: + fields[field_name] = GraphQLObjectField( + name=fields_data.fields_names[field_name], + description=fields_data.fields_descriptions.get(field_name), + field_type=fields_data.fields_types[field_name], + args=fields_data.fields_args.get(field_name), + resolver=fields_data.fields_resolvers.get(field_name), + subscriber=fields_data.fields_subscribers.get(field_name), + default_value=fields_data.fields_defaults.get(field_name), + ) + return fields + + @classmethod + def _process_type_hints_and_aliases(cls, fields_data: GraphQLFieldData): + fields_data.type_hints.update(cls.__annotations__) # pylint: disable=no-member + fields_data.aliases.update(getattr(cls, "__aliases__", None) or {}) + fields_data.aliases_targets = list(fields_data.aliases.values()) + + for attr_name, attr_type in fields_data.type_hints.items(): + if attr_name.startswith("__"): continue - fields_order.append(attr_name) + if attr_name in fields_data.aliases_targets: + cls_attr = getattr(cls, attr_name, None) + if not isinstance(cls_attr, GraphQLObjectField): + continue - fields_names[attr_name] = convert_python_name_to_graphql(attr_name) - fields_types[attr_name] = attr_type + fields_data.fields_order.append(attr_name) + fields_data.fields_names[attr_name] = convert_python_name_to_graphql( + attr_name + ) + fields_data.fields_types[attr_name] = attr_type - def process_class_attributes(target_cls): + @staticmethod + def _process_class_attributes(target_cls, fields_data: GraphQLFieldData): for attr_name in dir(target_cls): if attr_name.startswith("__"): continue cls_attr = getattr(target_cls, attr_name) if isinstance(cls_attr, GraphQLObjectField): - if attr_name not in fields_order: - fields_order.append(attr_name) + if attr_name not in fields_data.fields_order: + fields_data.fields_order.append(attr_name) - fields_names[attr_name] = ( + fields_data.fields_names[attr_name] = ( cls_attr.name or convert_python_name_to_graphql(attr_name) ) if cls_attr.field_type: - fields_types[attr_name] = cls_attr.field_type + fields_data.fields_types[attr_name] = cls_attr.field_type if cls_attr.description: - fields_descriptions[attr_name] = cls_attr.description + fields_data.fields_descriptions[attr_name] = cls_attr.description if cls_attr.resolver: - fields_resolvers[attr_name] = cls_attr.resolver + fields_data.fields_resolvers[attr_name] = cls_attr.resolver field_args = get_field_args_from_resolver(cls_attr.resolver) if field_args: - fields_args[attr_name] = update_field_args_options( + fields_data.fields_args[attr_name] = update_field_args_options( field_args, cls_attr.args ) if cls_attr.default_value: - fields_defaults[attr_name] = cls_attr.default_value + fields_data.fields_defaults[attr_name] = cls_attr.default_value elif isinstance(cls_attr, GraphQLObjectResolver): - if cls_attr.field_type and cls_attr.field not in fields_types: - fields_types[cls_attr.field] = cls_attr.field_type - if cls_attr.description and cls_attr.field not in fields_descriptions: - fields_descriptions[cls_attr.field] = cls_attr.description - fields_resolvers[cls_attr.field] = cls_attr.resolver + if ( + cls_attr.field_type + and cls_attr.field not in fields_data.fields_types + ): + fields_data.fields_types[cls_attr.field] = cls_attr.field_type + if ( + cls_attr.description + and cls_attr.field not in fields_data.fields_descriptions + ): + fields_data.fields_descriptions[cls_attr.field] = ( + cls_attr.description + ) + fields_data.fields_resolvers[cls_attr.field] = cls_attr.resolver field_args = get_field_args_from_resolver(cls_attr.resolver) - if field_args and not fields_args.get(cls_attr.field): - fields_args[cls_attr.field] = update_field_args_options( + if field_args and not fields_data.fields_args.get(cls_attr.field): + fields_data.fields_args[cls_attr.field] = update_field_args_options( field_args, cls_attr.args ) elif isinstance(cls_attr, GraphQLObjectSource): - if cls_attr.field_type and cls_attr.field not in fields_types: - fields_types[cls_attr.field] = cls_attr.field_type - if cls_attr.description and cls_attr.field not in fields_descriptions: - fields_descriptions[cls_attr.field] = cls_attr.description - fields_subscribers[cls_attr.field] = cls_attr.subscriber + if ( + cls_attr.field_type + and cls_attr.field not in fields_data.fields_types + ): + fields_data.fields_types[cls_attr.field] = cls_attr.field_type + if ( + cls_attr.description + and cls_attr.field not in fields_data.fields_descriptions + ): + fields_data.fields_descriptions[cls_attr.field] = ( + cls_attr.description + ) + fields_data.fields_subscribers[cls_attr.field] = cls_attr.subscriber field_args = get_field_args_from_subscriber(cls_attr.subscriber) if field_args: - fields_args[cls_attr.field] = update_field_args_options( + fields_data.fields_args[cls_attr.field] = update_field_args_options( field_args, cls_attr.args ) - elif attr_name not in aliases_targets and not callable(cls_attr): - fields_defaults[attr_name] = cls_attr - - for interface in getattr(cls, "__implements__", []): - process_class_attributes(interface) - - process_class_attributes(cls) - fields: Dict[str, "GraphQLObjectField"] = {} - for field_name in fields_order: - fields[field_name] = GraphQLObjectField( - name=fields_names[field_name], - description=fields_descriptions.get(field_name), - field_type=fields_types[field_name], - args=fields_args.get(field_name), - resolver=fields_resolvers.get(field_name), - subscriber=fields_subscribers.get(field_name), - default_value=fields_defaults.get(field_name), - ) - return GraphQLObjectData(fields=fields, interfaces=interfaces_names) + elif attr_name not in fields_data.aliases_targets and not callable( + cls_attr + ): + fields_data.fields_defaults[attr_name] = cls_attr + + @classmethod + def _collect_inherited_objects(cls): + raise NotImplementedError diff --git a/ariadne_graphql_modules/next/graphql_object/utils.py b/ariadne_graphql_modules/base_object_type/utils.py similarity index 97% rename from ariadne_graphql_modules/next/graphql_object/utils.py rename to ariadne_graphql_modules/base_object_type/utils.py index e562287..a3d9025 100644 --- a/ariadne_graphql_modules/next/graphql_object/utils.py +++ b/ariadne_graphql_modules/base_object_type/utils.py @@ -10,14 +10,14 @@ from ..description import get_description_node from ..typing import get_type_node from ..value import get_value_node -from .object_field import GraphQLObjectField, GraphQLObjectFieldArg +from .graphql_field import GraphQLObjectField, GraphQLObjectFieldArg if TYPE_CHECKING: - from .object_type import GraphQLObject + from .graphql_type import GraphQLBaseObject def get_field_node_from_obj_field( - parent_type: Type["GraphQLObject"], + parent_type: Type["GraphQLBaseObject"], metadata: GraphQLMetadata, field: GraphQLObjectField, ) -> FieldDefinitionNode: diff --git a/ariadne_graphql_modules/next/graphql_object/validators.py b/ariadne_graphql_modules/base_object_type/validators.py similarity index 95% rename from ariadne_graphql_modules/next/graphql_object/validators.py rename to ariadne_graphql_modules/base_object_type/validators.py index 672ad98..f104655 100644 --- a/ariadne_graphql_modules/next/graphql_object/validators.py +++ b/ariadne_graphql_modules/base_object_type/validators.py @@ -4,7 +4,7 @@ from graphql import FieldDefinitionNode, ObjectTypeDefinitionNode, TypeDefinitionNode from ..convert_name import convert_python_name_to_graphql -from .object_field import ( +from .graphql_field import ( GraphQLObjectField, GraphQLObjectFieldArg, GraphQLObjectResolver, @@ -16,10 +16,10 @@ ) from ..validators import validate_description, validate_name from ..value import get_value_node -from ...utils import parse_definition +from ..utils import parse_definition if TYPE_CHECKING: - from .object_type import GraphQLObject + from .graphql_type import GraphQLBaseObject @dataclass @@ -31,8 +31,15 @@ class GraphQLObjectValidationData: sources_instances: Dict[str, GraphQLObjectSource] +def get_all_annotations(cls): + annotations = {} + for base_cls in reversed(cls.__mro__): + annotations.update(getattr(base_cls, "__annotations__", {})) + return annotations + + def validate_object_type_with_schema( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], valid_type: Type[TypeDefinitionNode] = ObjectTypeDefinitionNode, ) -> Dict[str, Any]: definition = cast( @@ -212,7 +219,9 @@ def validate_object_type_with_schema( return get_object_type_with_schema_kwargs(cls, aliases, field_names) -def validate_object_type_without_schema(cls: Type["GraphQLObject"]) -> Dict[str, Any]: +def validate_object_type_without_schema( + cls: Type["GraphQLBaseObject"], +) -> Dict[str, Any]: data = get_object_type_validation_data(cls) # Alias target is not present in schema as a field if its not an @@ -246,7 +255,7 @@ def validate_object_type_without_schema(cls: Type["GraphQLObject"]) -> Dict[str, def validate_object_unique_graphql_names( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], fields_attrs: List[str], fields_instances: Dict[str, GraphQLObjectField], ): @@ -272,7 +281,7 @@ def validate_object_unique_graphql_names( def validate_object_resolvers( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], fields_names: List[str], fields_instances: Dict[str, GraphQLObjectField], resolvers_instances: Dict[str, GraphQLObjectResolver], @@ -317,7 +326,7 @@ def validate_object_resolvers( def validate_object_subscribers( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], fields_names: List[str], sources_instances: Dict[str, GraphQLObjectSource], ): @@ -356,7 +365,7 @@ def validate_object_subscribers( ) -def validate_object_fields_args(cls: Type["GraphQLObject"]): +def validate_object_fields_args(cls: Type["GraphQLBaseObject"]): for field_name in dir(cls): field_instance = getattr(cls, field_name) if ( @@ -367,7 +376,7 @@ def validate_object_fields_args(cls: Type["GraphQLObject"]): def validate_object_field_args( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], field_name: str, field_instance: Union["GraphQLObjectField", "GraphQLObjectResolver"], ): @@ -411,7 +420,7 @@ def validate_object_field_args( def validate_object_aliases( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], aliases: Dict[str, str], fields_names: List[str], fields_resolvers: List[str], @@ -432,7 +441,7 @@ def validate_object_aliases( def validate_field_arg_default_value( - cls: Type["GraphQLObject"], field_name: str, arg_name: str, default_value: Any + cls: Type["GraphQLBaseObject"], field_name: str, arg_name: str, default_value: Any ): if default_value is None: return @@ -449,10 +458,12 @@ def validate_field_arg_default_value( def get_object_type_validation_data( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], ) -> GraphQLObjectValidationData: fields_attrs: List[str] = [ - attr_name for attr_name in cls.__annotations__ if not attr_name.startswith("__") + attr_name + for attr_name in get_all_annotations(cls) + if not attr_name.startswith("__") ] fields_instances: Dict[str, GraphQLObjectField] = {} @@ -494,12 +505,12 @@ def get_object_type_validation_data( def get_object_type_kwargs( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], aliases: Dict[str, str], ) -> Dict[str, Any]: kwargs: Dict[str, Any] = {} - for attr_name in cls.__annotations__: + for attr_name in get_all_annotations(cls): if attr_name.startswith("__"): continue @@ -527,7 +538,7 @@ def get_object_type_kwargs( def get_object_type_with_schema_kwargs( - cls: Type["GraphQLObject"], + cls: Type["GraphQLBaseObject"], aliases: Dict[str, str], field_names: List[str], ) -> Dict[str, Any]: diff --git a/ariadne_graphql_modules/next/compatibility_layer.py b/ariadne_graphql_modules/compatibility_layer.py similarity index 92% rename from ariadne_graphql_modules/next/compatibility_layer.py rename to ariadne_graphql_modules/compatibility_layer.py index 8106dc5..182e2a0 100644 --- a/ariadne_graphql_modules/next/compatibility_layer.py +++ b/ariadne_graphql_modules/compatibility_layer.py @@ -12,20 +12,19 @@ UnionTypeDefinitionNode, ) -from ..executable_schema import get_all_types +from .v1.executable_schema import get_all_types + +from .v1.directive_type import DirectiveType +from .v1.enum_type import EnumType +from .v1.input_type import InputType +from .v1.interface_type import InterfaceType +from .v1.mutation_type import MutationType +from .v1.scalar_type import ScalarType +from .v1.subscription_type import SubscriptionType +from .v1.union_type import UnionType +from .v1.object_type import ObjectType +from .v1.bases import BindableType -from ..directive_type import DirectiveType -from ..enum_type import EnumType -from ..input_type import InputType -from ..interface_type import InterfaceType -from ..mutation_type import MutationType -from ..scalar_type import ScalarType -from ..subscription_type import SubscriptionType -from ..union_type import UnionType - -from ..object_type import ObjectType - -from ..bases import BindableType from .base import GraphQLModel, GraphQLType from . import ( GraphQLObjectModel, diff --git a/ariadne_graphql_modules/next/convert_name.py b/ariadne_graphql_modules/convert_name.py similarity index 100% rename from ariadne_graphql_modules/next/convert_name.py rename to ariadne_graphql_modules/convert_name.py diff --git a/ariadne_graphql_modules/next/deferredtype.py b/ariadne_graphql_modules/deferredtype.py similarity index 100% rename from ariadne_graphql_modules/next/deferredtype.py rename to ariadne_graphql_modules/deferredtype.py diff --git a/ariadne_graphql_modules/next/description.py b/ariadne_graphql_modules/description.py similarity index 100% rename from ariadne_graphql_modules/next/description.py rename to ariadne_graphql_modules/description.py diff --git a/ariadne_graphql_modules/enum_type/__init__.py b/ariadne_graphql_modules/enum_type/__init__.py new file mode 100644 index 0000000..2782ef5 --- /dev/null +++ b/ariadne_graphql_modules/enum_type/__init__.py @@ -0,0 +1,9 @@ +from .graphql_type import GraphQLEnum, graphql_enum, create_graphql_enum_model +from .models import GraphQLEnumModel + +__all__ = [ + "GraphQLEnum", + "GraphQLEnumModel", + "create_graphql_enum_model", + "graphql_enum", +] diff --git a/ariadne_graphql_modules/next/graphql_enum_type/enum_type.py b/ariadne_graphql_modules/enum_type/graphql_type.py similarity index 99% rename from ariadne_graphql_modules/next/graphql_enum_type/enum_type.py rename to ariadne_graphql_modules/enum_type/graphql_type.py index 84a12e8..fc33745 100644 --- a/ariadne_graphql_modules/next/graphql_enum_type/enum_type.py +++ b/ariadne_graphql_modules/enum_type/graphql_type.py @@ -5,9 +5,9 @@ from graphql import EnumTypeDefinitionNode, EnumValueDefinitionNode, NameNode from ..base import GraphQLMetadata, GraphQLModel, GraphQLType from ..description import get_description_node -from ..graphql_enum_type.enum_model import GraphQLEnumModel +from .models import GraphQLEnumModel from ..validators import validate_description, validate_name -from ...utils import parse_definition +from ..utils import parse_definition class GraphQLEnum(GraphQLType): diff --git a/ariadne_graphql_modules/next/graphql_enum_type/enum_model.py b/ariadne_graphql_modules/enum_type/models.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_enum_type/enum_model.py rename to ariadne_graphql_modules/enum_type/models.py diff --git a/ariadne_graphql_modules/enum_type/type.py b/ariadne_graphql_modules/enum_type/type.py new file mode 100644 index 0000000..fc33745 --- /dev/null +++ b/ariadne_graphql_modules/enum_type/type.py @@ -0,0 +1,343 @@ +from enum import Enum +from inspect import isclass +from typing import Any, Dict, Iterable, List, Optional, Set, Type, Union, cast + +from graphql import EnumTypeDefinitionNode, EnumValueDefinitionNode, NameNode +from ..base import GraphQLMetadata, GraphQLModel, GraphQLType +from ..description import get_description_node +from .models import GraphQLEnumModel +from ..validators import validate_description, validate_name +from ..utils import parse_definition + + +class GraphQLEnum(GraphQLType): + __abstract__: bool = True + __schema__: Optional[str] + __description__: Optional[str] + __members__: Optional[Union[Type[Enum], Dict[str, Any], List[str]]] + __members_descriptions__: Optional[Dict[str, str]] + + def __init_subclass__(cls) -> None: + super().__init_subclass__() + + if cls.__dict__.get("__abstract__"): + return + + cls.__abstract__ = False + cls._validate() + + @classmethod + def __get_graphql_model__(cls, metadata: GraphQLMetadata) -> "GraphQLModel": + name = cls.__get_graphql_name__() + + if getattr(cls, "__schema__", None): + return cls.__get_graphql_model_with_schema__(name) + + return cls.__get_graphql_model_without_schema__(name) + + @classmethod + def __get_graphql_model_with_schema__(cls, name: str) -> "GraphQLEnumModel": + definition: EnumTypeDefinitionNode = cast( + EnumTypeDefinitionNode, + parse_definition(EnumTypeDefinitionNode, cls.__schema__), + ) + + members = getattr(cls, "__members__", []) + members_values: Dict[str, Any] = {} + + if isinstance(members, dict): + members_values = dict(members.items()) + elif isclass(members) and issubclass(members, Enum): + members_values = {member.name: member for member in members} + else: + members_values = { + value.name.value: value.name.value + for value in definition.values # pylint: disable=no-member + } + + members_descriptions = getattr(cls, "__members_descriptions__", {}) + + return GraphQLEnumModel( + name=name, + members=members_values, + ast_type=EnumTypeDefinitionNode, + ast=EnumTypeDefinitionNode( + name=NameNode(value=name), + directives=definition.directives, + description=definition.description + or (get_description_node(getattr(cls, "__description__", None))), + values=tuple( + EnumValueDefinitionNode( + name=value.name, + directives=value.directives, + description=value.description + or ( + get_description_node( + members_descriptions.get(value.name.value), + ) + ), + ) + for value in definition.values # pylint: disable=no-member + ), + ), + ) + + @classmethod + def __get_graphql_model_without_schema__(cls, name: str) -> "GraphQLEnumModel": + members = getattr(cls, "__members__", []) + members_values = {} + if isinstance(members, dict): + members_values = dict(members.items()) + elif isclass(members) and issubclass(members, Enum): + members_values = {i.name: i for i in members} + elif isinstance(members, list): + members_values = {kv: kv for kv in members} + + members_descriptions = getattr(cls, "__members_descriptions__", {}) + + return GraphQLEnumModel( + name=name, + members=members_values, + ast_type=EnumTypeDefinitionNode, + ast=EnumTypeDefinitionNode( + name=NameNode(value=name), + description=get_description_node( + getattr(cls, "__description__", None), + ), + values=tuple( + EnumValueDefinitionNode( + name=NameNode(value=value_name), + description=get_description_node( + members_descriptions.get(value_name) + ), + ) + for value_name in members_values + ), + ), + ) + + @classmethod + def _validate(cls): + if getattr(cls, "__schema__", None): + cls._validate_enum_type_with_schema() + else: + cls._validate_enum_type() + + @classmethod + def _validate_enum_type_with_schema(cls): + definition = parse_definition(EnumTypeDefinitionNode, cls.__schema__) + + if not isinstance(definition, EnumTypeDefinitionNode): + raise ValueError( + f"Class '{cls.__name__}' defines '__schema__' attribute " + f"with declaration for an invalid GraphQL type. " + f"('{definition.__class__.__name__}' != '{EnumTypeDefinitionNode.__name__}')" + ) + + validate_name(cls, definition) + validate_description(cls, definition) + + members_names = { + value.name.value for value in definition.values # pylint: disable=no-member + } + if not members_names: + raise ValueError( + f"Class '{cls.__name__}' defines '__schema__' attribute " + "that doesn't declare any enum members." + ) + + members_values = getattr(cls, "__members__", None) + if members_values: + cls.validate_members_values(members_values, members_names) + + members_descriptions = getattr(cls, "__members_descriptions__", {}) + cls.validate_enum_members_descriptions(members_names, members_descriptions) + + duplicate_descriptions = [ + ast_member.name.value + for ast_member in definition.values # pylint: disable=no-member + if ast_member.description + and ast_member.description.value + and members_descriptions.get(ast_member.name.value) + ] + + if duplicate_descriptions: + raise ValueError( + f"Class '{cls.__name__}' '__members_descriptions__' attribute defines " + f"descriptions for enum members that also have description in '__schema__' " + f"attribute. (members: '{', '.join(duplicate_descriptions)}')" + ) + + @classmethod + def validate_members_values(cls, members_values, members_names): + if isinstance(members_values, list): + raise ValueError( + f"Class '{cls.__name__}' '__members__' attribute " + "can't be a list when used together with '__schema__'." + ) + + missing_members = None + if isinstance(members_values, dict): + missing_members = members_names - set(members_values) + elif isclass(members_values) and issubclass(members_values, Enum): + missing_members = members_names - {value.name for value in members_values} + + if missing_members: + raise ValueError( + f"Class '{cls.__name__}' '__members__' is missing values " + f"for enum members defined in '__schema__'. " + f"(missing items: '{', '.join(missing_members)}')" + ) + + @classmethod + def _validate_enum_type(cls): + members_values = getattr(cls, "__members__", None) + if not members_values: + raise ValueError( + f"Class '{cls.__name__}' '__members__' attribute is either missing or " + "empty. Either define it or provide full SDL for this enum using " + "the '__schema__' attribute." + ) + + if not any( + [ + isinstance(members_values, (dict, list)), + isclass(members_values) and issubclass(members_values, Enum), + ] + ): + raise ValueError( + f"Class '{cls.__name__}' '__members__' attribute is of unsupported type. " + f"Expected 'Dict[str, Any]', 'Type[Enum]' or List[str]. " + f"(found: '{type(members_values)}')" + ) + + members_names = cls.get_members_set(members_values) + members_descriptions = getattr(cls, "__members_descriptions__", {}) + cls.validate_enum_members_descriptions(members_names, members_descriptions) + + @classmethod + def validate_enum_members_descriptions( + cls, members: Set[str], members_descriptions: dict + ): + invalid_descriptions = set(members_descriptions) - members + if invalid_descriptions: + invalid_descriptions_str = "', '".join(invalid_descriptions) + raise ValueError( + f"Class '{cls.__name__}' '__members_descriptions__' attribute defines " + f"descriptions for undefined enum members. " + f"(undefined members: '{invalid_descriptions_str}')" + ) + + @staticmethod + def get_members_set( + members: Optional[Union[Type[Enum], Dict[str, Any], List[str]]] + ) -> Set[str]: + if isinstance(members, dict): + return set(members.keys()) + + if isclass(members) and issubclass(members, Enum): + return set(member.name for member in members) + + if isinstance(members, list): + return set(members) + + raise TypeError( + f"Expected members to be of type Dict[str, Any], List[str], or Enum." + f"Got {type(members).__name__} instead." + ) + + +def create_graphql_enum_model( + enum: Type[Enum], + *, + name: Optional[str] = None, + description: Optional[str] = None, + members_descriptions: Optional[Dict[str, str]] = None, + members_include: Optional[Iterable[str]] = None, + members_exclude: Optional[Iterable[str]] = None, +) -> "GraphQLEnumModel": + if members_include and members_exclude: + raise ValueError( + "'members_include' and 'members_exclude' options are mutually exclusive." + ) + + if hasattr(enum, "__get_graphql_model__"): + return cast(GraphQLEnumModel, enum.__get_graphql_model__()) + + if not name: + if hasattr(enum, "__get_graphql_name__"): + name = cast("str", enum.__get_graphql_name__()) + else: + name = enum.__name__ + + members: Dict[str, Any] = {i.name: i for i in enum} + final_members: Dict[str, Any] = {} + + if members_include: + for key, value in members.items(): + if key in members_include: + final_members[key] = value + elif members_exclude: + for key, value in members.items(): + if key not in members_exclude: + final_members[key] = value + else: + final_members = members + + members_descriptions = members_descriptions or {} + for member in members_descriptions: + if member not in final_members: + raise ValueError( + f"Member description was specified for a member '{member}' " + "not present in final GraphQL enum." + ) + + return GraphQLEnumModel( + name=name, + members=final_members, + ast_type=EnumTypeDefinitionNode, + ast=EnumTypeDefinitionNode( + name=NameNode(value=name), + description=get_description_node(description), + values=tuple( + EnumValueDefinitionNode( + name=NameNode(value=value_name), + description=get_description_node( + members_descriptions.get(value_name) + ), + ) + for value_name in final_members + ), + ), + ) + + +def graphql_enum( + cls: Optional[Type[Enum]] = None, + *, + name: Optional[str] = None, + description: Optional[str] = None, + members_descriptions: Optional[Dict[str, str]] = None, + members_include: Optional[Iterable[str]] = None, + members_exclude: Optional[Iterable[str]] = None, +): + def graphql_enum_decorator(cls: Type[Enum]): + graphql_model = create_graphql_enum_model( + cls, + name=name, + description=description, + members_descriptions=members_descriptions, + members_include=members_include, + members_exclude=members_exclude, + ) + + def __get_graphql_model__(*_) -> GraphQLEnumModel: + return graphql_model + + setattr(cls, "__get_graphql_model__", classmethod(__get_graphql_model__)) + return cls + + if cls: + return graphql_enum_decorator(cls) + + return graphql_enum_decorator diff --git a/ariadne_graphql_modules/executable_schema.py b/ariadne_graphql_modules/executable_schema.py index 1914742..79da59f 100644 --- a/ariadne_graphql_modules/executable_schema.py +++ b/ariadne_graphql_modules/executable_schema.py @@ -1,236 +1,243 @@ -from typing import ( - Dict, - List, - Optional, - Sequence, - Tuple, - Type, - Union, - cast, -) +from enum import Enum +from typing import Any, Dict, List, Optional, Sequence, Type, Union from ariadne import ( SchemaBindable, SchemaDirectiveVisitor, + SchemaNameConverter, + convert_schema_names, repair_schema_default_enum_values, validate_schema_default_enum_values, ) from graphql import ( - ConstDirectiveNode, DocumentNode, - FieldDefinitionNode, GraphQLSchema, - NamedTypeNode, - ObjectTypeDefinitionNode, - TypeDefinitionNode, assert_valid_schema, build_ast_schema, - concat_ast, parse, + concat_ast, ) -from graphql.language import ast -from .bases import BaseType, BindableType, DeferredType, DefinitionType -from .enum_type import EnumType +from .base import GraphQLMetadata, GraphQLModel, GraphQLType +from .roots import ROOTS_NAMES, merge_root_nodes +from .sort import sort_schema_document -ROOT_TYPES = ["Query", "Mutation", "Subscription"] +SchemaType = Union[str, Enum, SchemaBindable, Type[GraphQLType], Type[Enum]] def make_executable_schema( - *args: Union[Type[BaseType], SchemaBindable, str], + *types: SchemaType, + directives: Optional[Dict[str, Type[SchemaDirectiveVisitor]]] = None, + convert_names_case: Union[bool, SchemaNameConverter] = False, merge_roots: bool = True, - extra_directives: Optional[Dict[str, Type[SchemaDirectiveVisitor]]] = None, -): - all_types = get_all_types(args) - extra_defs = parse_extra_sdl(args) - extra_bindables: List[SchemaBindable] = [ - arg for arg in args if isinstance(arg, SchemaBindable) +) -> GraphQLSchema: + metadata = GraphQLMetadata() + type_defs: List[str] = find_type_defs(types) + types_list: List[SchemaType] = flatten_types(types, metadata) + + assert_types_unique(types_list, merge_roots) + assert_types_not_abstract(types_list) + + schema_bindables: List[Union[SchemaBindable, GraphQLModel]] = [] + for type_def in types_list: + if isinstance(type_def, SchemaBindable): + schema_bindables.append(type_def) + elif isinstance(type_def, type) and issubclass(type_def, (GraphQLType, Enum)): + schema_bindables.append(metadata.get_graphql_model(type_def)) + + schema_models: List[GraphQLModel] = [ + type_def for type_def in schema_bindables if isinstance(type_def, GraphQLModel) ] - type_defs: List[Type[DefinitionType]] = [] - for type_ in all_types: - if issubclass(type_, DefinitionType): - type_defs.append(type_) + models_document: Optional[DocumentNode] = None + type_defs_document: Optional[DocumentNode] = None - validate_no_missing_definitions(all_types, type_defs, extra_defs) + if schema_models: + models_document = DocumentNode( + definitions=tuple(schema_model.ast for schema_model in schema_models), + ) + + if type_defs: + type_defs_document = parse("\n".join(type_defs)) + + if models_document and type_defs_document: + document_node = concat_ast((models_document, type_defs_document)) + elif models_document: + document_node = models_document + elif type_defs_document: + document_node = type_defs_document + else: + raise ValueError( + "'make_executable_schema' was called without any GraphQL types." + ) - schema = build_schema(type_defs, extra_defs, merge_roots) + if merge_roots: + document_node = merge_root_nodes(document_node) - if extra_bindables: - for bindable in extra_bindables: - bindable.bind_to_schema(schema) + document_node = sort_schema_document(document_node) + schema = build_ast_schema(document_node) - if extra_directives: - SchemaDirectiveVisitor.visit_schema_directives(schema, extra_directives) + if directives: + SchemaDirectiveVisitor.visit_schema_directives(schema, directives) assert_valid_schema(schema) validate_schema_default_enum_values(schema) repair_schema_default_enum_values(schema) - add_directives_to_schema(schema, type_defs) + for schema_bindable in schema_bindables: + schema_bindable.bind_to_schema(schema) + + if convert_names_case: + convert_schema_names( + schema, + convert_names_case if callable(convert_names_case) else None, + ) return schema -def get_all_types( - args: Sequence[Union[Type[BaseType], SchemaBindable, str]] -) -> List[Type[BaseType]]: - all_types: List[Type[BaseType]] = [] - for arg in args: - if isinstance(arg, (str, SchemaBindable)): - continue # Skip args of unsupported types +def find_type_defs(types: Sequence[SchemaType]) -> List[str]: + type_defs: List[str] = [] - for child_type in arg.__get_types__(): - if child_type not in all_types: - all_types.append(child_type) - return all_types + for type_def in types: + if isinstance(type_def, str): + type_defs.append(type_def) + elif isinstance(type_def, list): + type_defs += find_type_defs(type_def) + return type_defs -def parse_extra_sdl( - args: Sequence[Union[Type[BaseType], SchemaBindable, str]] -) -> List[TypeDefinitionNode]: - sdl_strings: List[str] = [cast(str, arg) for arg in args if isinstance(arg, str)] - if not sdl_strings: - return [] - extra_sdl = "\n\n".join(sdl_strings) - return cast( - List[TypeDefinitionNode], - list(parse(extra_sdl).definitions), +def flatten_types( + types: Sequence[SchemaType], + metadata: GraphQLMetadata, +) -> List[SchemaType]: + flat_schema_types_list: List[SchemaType] = flatten_schema_types( + types, metadata, dedupe=True ) + types_list: List[SchemaType] = [] + for type_def in flat_schema_types_list: + if isinstance(type_def, SchemaBindable): + types_list.append(type_def) -def validate_no_missing_definitions( - all_types: List[Type[BaseType]], - type_defs: List[Type[DefinitionType]], - extra_defs: List[TypeDefinitionNode], -): - deferred_names: List[str] = [] - for type_ in all_types: - if isinstance(type_, DeferredType): - deferred_names.append(type_.graphql_name) - - real_names = [type_.graphql_name for type_ in type_defs] - real_names += [definition.name.value for definition in extra_defs] - - missing_names = set(deferred_names) - set(real_names) - if missing_names: - raise ValueError( - "Following types are defined as deferred and are missing " - f"from schema: {', '.join(missing_names)}" - ) + elif isinstance(type_def, type) and issubclass(type_def, GraphQLType): + type_name = type_def.__name__ + if getattr(type_def, "__abstract__", None): + raise ValueError( + f"Type '{type_name}' is an abstract type and can't be used " + "for schema creation." + ) -def build_schema( - type_defs: List[Type[DefinitionType]], - extra_defs: List[TypeDefinitionNode], - merge_roots: bool = True, -) -> GraphQLSchema: - schema_definitions: List[ast.DocumentNode] = [] - if merge_roots: - schema_definitions.append(build_root_schema(type_defs, extra_defs)) - for type_ in type_defs: - if type_.graphql_name not in ROOT_TYPES or not merge_roots: - schema_definitions.append(parse(type_.__schema__)) - for extra_type_def in extra_defs: - if extra_type_def.name.value not in ROOT_TYPES or not merge_roots: - schema_definitions.append(DocumentNode(definitions=(extra_type_def,))) + types_list.append(type_def) - ast_document = concat_ast(schema_definitions) - schema = build_ast_schema(ast_document) + elif isinstance(type_def, type) and issubclass(type_def, Enum): + types_list.append(type_def) - for type_ in type_defs: - if issubclass(type_, BindableType): - type_.__bind_to_schema__(schema) + elif isinstance(type_def, list): + types_list += find_type_defs(type_def) - return schema + return types_list -RootTypeDef = Tuple[str, DocumentNode] +def flatten_schema_types( + types: Sequence[Union[SchemaType, List[SchemaType]]], + metadata: GraphQLMetadata, + dedupe: bool, +) -> List[SchemaType]: + flat_list: List[SchemaType] = [] + checked_types: List[Type[GraphQLType]] = [] + for type_def in types: + if isinstance(type_def, str): + continue + if isinstance(type_def, list): + flat_list += flatten_schema_types(type_def, metadata, dedupe=False) + elif isinstance(type_def, SchemaBindable): + flat_list.append(type_def) + elif isinstance(type_def, type) and issubclass(type_def, Enum): + flat_list.append(type_def) + elif isinstance(type_def, type) and issubclass(type_def, GraphQLType): + add_graphql_type_to_flat_list(flat_list, checked_types, type_def, metadata) + elif get_graphql_type_name(type_def): + flat_list.append(type_def) -def build_root_schema( - type_defs: List[Type[DefinitionType]], - extra_defs: List[TypeDefinitionNode], -) -> DocumentNode: - root_types: Dict[str, List[RootTypeDef]] = { - "Query": [], - "Mutation": [], - "Subscription": [], - } + if not dedupe: + return flat_list - for type_def in type_defs: - if type_def.graphql_name in root_types: - root_types[type_def.graphql_name].append( - (type_def.__name__, parse(type_def.__schema__)) - ) + unique_list: List[SchemaType] = [] + for type_def in flat_list: + if type_def not in unique_list: + unique_list.append(type_def) - for extra_type_def in extra_defs: - if extra_type_def.name.value in root_types: - root_types[extra_type_def.name.value].append( - ("extra_sdl", DocumentNode(definitions=(extra_type_def,))) - ) + return unique_list - schema: List[DocumentNode] = [] - for root_name, root_type_defs in root_types.items(): - if len(root_type_defs) == 1: - schema.append(root_type_defs[0][1]) - elif root_type_defs: - schema.append(merge_root_types(root_name, root_type_defs)) - return concat_ast(schema) +def add_graphql_type_to_flat_list( + flat_list: List[SchemaType], + checked_types: List[Type[GraphQLType]], + type_def: Type[GraphQLType], + metadata: GraphQLMetadata, +) -> None: + if type_def in checked_types: + return + checked_types.append(type_def) -def merge_root_types(root_name: str, type_defs: List[RootTypeDef]) -> DocumentNode: - interfaces: List[NamedTypeNode] = [] - directives: List[ConstDirectiveNode] = [] - fields: Dict[str, Tuple[str, FieldDefinitionNode]] = {} + for child_type in type_def.__get_graphql_types__(metadata): + flat_list.append(child_type) - for type_source, type_def in type_defs: - type_definition = cast(ObjectTypeDefinitionNode, type_def.definitions[0]) - interfaces.extend(type_definition.interfaces) - directives.extend(type_definition.directives) + if issubclass(child_type, GraphQLType): + add_graphql_type_to_flat_list( + flat_list, checked_types, child_type, metadata + ) - for field_def in type_definition.fields: - field_name = field_def.name.value - if field_name in fields: - other_type_source = fields[field_name][0] - raise ValueError( - f"Multiple {root_name} types are defining same field " - f"'{field_name}': {other_type_source}, {type_source}" - ) - fields[field_name] = (type_source, field_def) +def get_graphql_type_name(type_def: SchemaType) -> Optional[str]: + if isinstance(type_def, SchemaBindable): + return None - merged_definition = ast.ObjectTypeDefinitionNode() - merged_definition.name = ast.NameNode() - merged_definition.name.value = root_name - merged_definition.interfaces = tuple(interfaces) - merged_definition.directives = tuple(directives) - merged_definition.fields = tuple( - fields[field_name][1] for field_name in sorted(fields) - ) + if isinstance(type_def, type) and issubclass(type_def, Enum): + return type_def.__name__ - merged_document = DocumentNode() - merged_document.definitions = (merged_definition,) + if isinstance(type_def, type) and issubclass(type_def, GraphQLType): + return type_def.__get_graphql_name__() - return merged_document + return None -def add_directives_to_schema( - schema: GraphQLSchema, type_defs: List[Type[DefinitionType]] -): - directives: Dict[str, Type[SchemaDirectiveVisitor]] = {} +def assert_types_unique(type_defs: List[SchemaType], merge_roots: bool): + types_names: Dict[str, Any] = {} for type_def in type_defs: - visitor = getattr(type_def, "__visitor__", None) - if visitor and issubclass(visitor, SchemaDirectiveVisitor): - directives[type_def.graphql_name] = visitor + type_name = get_graphql_type_name(type_def) + if not type_name: + continue + + if merge_roots and type_name in ROOTS_NAMES: + continue + + if type_name in types_names: + type_def_name = getattr(type_def, "__name__") or type_def + raise ValueError( + f"Types '{type_def_name}' and '{types_names[type_name]}' both define " + f"GraphQL type with name '{type_name}'." + ) - if directives: - SchemaDirectiveVisitor.visit_schema_directives(schema, directives) + types_names[type_name] = type_def -def repair_default_enum_values(schema, types_list: List[Type[DefinitionType]]) -> None: - for type_ in types_list: - if issubclass(type_, EnumType): - type_.__bind_to_default_values__(schema) +def assert_types_not_abstract(type_defs: List[SchemaType]): + for type_def in type_defs: + if isinstance(type_def, SchemaBindable): + continue + + if ( + isinstance(type_def, type) + and issubclass(type_def, GraphQLType) + and getattr(type_def, "__abstract__", None) + ): + raise ValueError( + f"Type '{type_def.__name__}' is an abstract type and can't be used " + "for schema creation." + ) diff --git a/ariadne_graphql_modules/next/idtype.py b/ariadne_graphql_modules/idtype.py similarity index 100% rename from ariadne_graphql_modules/next/idtype.py rename to ariadne_graphql_modules/idtype.py diff --git a/ariadne_graphql_modules/input_type/__init__.py b/ariadne_graphql_modules/input_type/__init__.py new file mode 100644 index 0000000..23a8e12 --- /dev/null +++ b/ariadne_graphql_modules/input_type/__init__.py @@ -0,0 +1,8 @@ +from .graphql_type import GraphQLInput +from .models import GraphQLInputModel + + +__all__ = [ + "GraphQLInput", + "GraphQLInputModel", +] diff --git a/ariadne_graphql_modules/next/graphql_input/input_field.py b/ariadne_graphql_modules/input_type/graphql_field.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_input/input_field.py rename to ariadne_graphql_modules/input_type/graphql_field.py diff --git a/ariadne_graphql_modules/next/graphql_input/input_type.py b/ariadne_graphql_modules/input_type/graphql_type.py similarity index 97% rename from ariadne_graphql_modules/next/graphql_input/input_type.py rename to ariadne_graphql_modules/input_type/graphql_type.py index 6d03a05..9670ea4 100644 --- a/ariadne_graphql_modules/next/graphql_input/input_type.py +++ b/ariadne_graphql_modules/input_type/graphql_type.py @@ -4,21 +4,21 @@ from graphql import InputObjectTypeDefinitionNode, InputValueDefinitionNode, NameNode -from .input_field import GraphQLInputField +from .graphql_field import GraphQLInputField from ..base import GraphQLMetadata, GraphQLModel, GraphQLType from ..convert_name import ( convert_graphql_name_to_python, convert_python_name_to_graphql, ) from ..description import get_description_node -from ..graphql_input.input_model import GraphQLInputModel -from ..graphql_input.validators import ( +from .models import GraphQLInputModel +from .validators import ( validate_input_type, validate_input_type_with_schema, ) from ..typing import get_graphql_type, get_type_node from ..value import get_value_node -from ...utils import parse_definition +from ..utils import parse_definition class GraphQLInput(GraphQLType): diff --git a/ariadne_graphql_modules/next/graphql_input/input_model.py b/ariadne_graphql_modules/input_type/models.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_input/input_model.py rename to ariadne_graphql_modules/input_type/models.py diff --git a/ariadne_graphql_modules/next/graphql_input/validators.py b/ariadne_graphql_modules/input_type/validators.py similarity index 97% rename from ariadne_graphql_modules/next/graphql_input/validators.py rename to ariadne_graphql_modules/input_type/validators.py index 852a778..68432b8 100644 --- a/ariadne_graphql_modules/next/graphql_input/validators.py +++ b/ariadne_graphql_modules/input_type/validators.py @@ -4,11 +4,11 @@ from ..convert_name import convert_graphql_name_to_python from ..validators import validate_description, validate_name from ..value import get_value_from_node, get_value_node -from ...utils import parse_definition -from .input_field import GraphQLInputField +from ..utils import parse_definition +from .graphql_field import GraphQLInputField if TYPE_CHECKING: - from .input_type import GraphQLInput + from .graphql_type import GraphQLInput def validate_input_type_with_schema(cls: Type["GraphQLInput"]) -> Dict[str, Any]: diff --git a/ariadne_graphql_modules/interface_type/__init__.py b/ariadne_graphql_modules/interface_type/__init__.py new file mode 100644 index 0000000..0ec79d8 --- /dev/null +++ b/ariadne_graphql_modules/interface_type/__init__.py @@ -0,0 +1,8 @@ +from .graphql_type import GraphQLInterface +from .models import GraphQLInterfaceModel + + +__all__ = [ + "GraphQLInterface", + "GraphQLInterfaceModel", +] diff --git a/ariadne_graphql_modules/interface_type/graphql_type.py b/ariadne_graphql_modules/interface_type/graphql_type.py new file mode 100644 index 0000000..9a33c14 --- /dev/null +++ b/ariadne_graphql_modules/interface_type/graphql_type.py @@ -0,0 +1,147 @@ +from typing import Any, Dict, List, Optional, Tuple, cast + +from ariadne.types import Resolver +from graphql import ( + FieldDefinitionNode, + InterfaceTypeDefinitionNode, + NameNode, + NamedTypeNode, +) + +from ..base_object_type import ( + GraphQLFieldData, + GraphQLBaseObject, + GraphQLObjectData, + validate_object_type_with_schema, + validate_object_type_without_schema, +) +from ..types import GraphQLClassType + +from ..utils import parse_definition +from ..base import GraphQLMetadata +from ..description import get_description_node +from ..object_type import GraphQLObject +from .models import GraphQLInterfaceModel + + +class GraphQLInterface(GraphQLBaseObject): + __valid_type__ = InterfaceTypeDefinitionNode + __graphql_type__ = GraphQLClassType.INTERFACE + __abstract__ = True + __description__: Optional[str] = None + + def __init_subclass__(cls) -> None: + super().__init_subclass__() + + if cls.__dict__.get("__abstract__"): + return + + cls.__abstract__ = False + + if cls.__dict__.get("__schema__"): + valid_type = getattr(cls, "__valid_type__", InterfaceTypeDefinitionNode) + cls.__kwargs__ = validate_object_type_with_schema(cls, valid_type) + else: + cls.__kwargs__ = validate_object_type_without_schema(cls) + + @classmethod + def __get_graphql_model_with_schema__(cls) -> "GraphQLInterfaceModel": + definition = cast( + InterfaceTypeDefinitionNode, + parse_definition(InterfaceTypeDefinitionNode, cls.__schema__), + ) + + resolvers: Dict[str, Resolver] = {} + fields: Tuple[FieldDefinitionNode, ...] = tuple() + fields, resolvers = cls._create_fields_and_resolvers_with_schema( + definition.fields + ) + + return GraphQLInterfaceModel( + name=definition.name.value, + ast_type=InterfaceTypeDefinitionNode, + ast=InterfaceTypeDefinitionNode( + name=NameNode(value=definition.name.value), + fields=tuple(fields), + interfaces=definition.interfaces, + ), + resolve_type=cls.resolve_type, + resolvers=resolvers, + aliases=getattr(cls, "__aliases__", {}), + out_names={}, + ) + + @classmethod + def __get_graphql_model_without_schema__( + cls, metadata: GraphQLMetadata, name: str + ) -> "GraphQLInterfaceModel": + type_data = cls.get_graphql_object_data(metadata) + type_aliases = getattr(cls, "__aliases__", None) or {} + + fields_ast: List[FieldDefinitionNode] = [] + resolvers: Dict[str, Resolver] = {} + aliases: Dict[str, str] = {} + out_names: Dict[str, Dict[str, str]] = {} + + fields_ast, resolvers, aliases, out_names = cls._process_graphql_fields( + metadata, type_data, type_aliases + ) + + interfaces_ast: List[NamedTypeNode] = [] + for interface_name in type_data.interfaces: + interfaces_ast.append(NamedTypeNode(name=NameNode(value=interface_name))) + + return GraphQLInterfaceModel( + name=name, + ast_type=InterfaceTypeDefinitionNode, + ast=InterfaceTypeDefinitionNode( + name=NameNode(value=name), + description=get_description_node( + getattr(cls, "__description__", None), + ), + fields=tuple(fields_ast), + interfaces=tuple(interfaces_ast), + ), + resolve_type=cls.resolve_type, + resolvers=resolvers, + aliases=aliases, + out_names=out_names, + ) + + @staticmethod + def resolve_type(obj: Any, *_) -> str: + if isinstance(obj, GraphQLObject): + return obj.__get_graphql_name__() + + raise ValueError( + f"Cannot resolve GraphQL type {obj} for object of type '{type(obj).__name__}'." + ) + + @classmethod + def _collect_inherited_objects(cls): + return [ + inherited_obj + for inherited_obj in cls.__mro__[1:] + if getattr(inherited_obj, "__graphql_type__", None) + == GraphQLClassType.INTERFACE + and not getattr(inherited_obj, "__abstract__", True) + ] + + @classmethod + def create_graphql_object_data_without_schema(cls) -> GraphQLObjectData: + fields_data = GraphQLFieldData() + inherited_objects = list(reversed(cls._collect_inherited_objects())) + + for inherited_obj in inherited_objects: + fields_data.type_hints.update(inherited_obj.__annotations__) + fields_data.aliases.update(getattr(inherited_obj, "__aliases__", {})) + + cls._process_type_hints_and_aliases(fields_data) + for inherited_obj in inherited_objects: + cls._process_class_attributes(inherited_obj, fields_data) + cls._process_class_attributes(cls, fields_data) + + return GraphQLObjectData( + fields=cls._build_fields(fields_data=fields_data), + interfaces=[], + ) diff --git a/ariadne_graphql_modules/next/graphql_interface/interface_model.py b/ariadne_graphql_modules/interface_type/models.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_interface/interface_model.py rename to ariadne_graphql_modules/interface_type/models.py diff --git a/ariadne_graphql_modules/next/__init__.py b/ariadne_graphql_modules/next/__init__.py deleted file mode 100644 index 6aea1ab..0000000 --- a/ariadne_graphql_modules/next/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -from .base import GraphQLMetadata, GraphQLModel, GraphQLType -from .convert_name import ( - convert_graphql_name_to_python, - convert_python_name_to_graphql, -) -from .deferredtype import deferred -from .description import get_description_node -from .graphql_enum_type import ( - GraphQLEnum, - GraphQLEnumModel, - create_graphql_enum_model, - graphql_enum, -) -from .executable_schema import make_executable_schema -from .idtype import GraphQLID -from .graphql_input import GraphQLInput, GraphQLInputModel -from .graphql_object import GraphQLObject, GraphQLObjectModel, object_field -from .roots import ROOTS_NAMES, merge_root_nodes -from .graphql_scalar import GraphQLScalar, GraphQLScalarModel -from .sort import sort_schema_document -from .graphql_union import GraphQLUnion, GraphQLUnionModel -from .value import get_value_from_node, get_value_node -from .graphql_interface import GraphQLInterface, GraphQLInterfaceModel -from .graphql_subscription import GraphQLSubscription, GraphQLSubscriptionModel - -__all__ = [ - "GraphQLEnum", - "GraphQLEnumModel", - "GraphQLID", - "GraphQLInput", - "GraphQLInputModel", - "GraphQLInterface", - "GraphQLInterfaceModel", - "GraphQLSubscription", - "GraphQLSubscriptionModel", - "GraphQLMetadata", - "GraphQLModel", - "GraphQLObject", - "GraphQLObjectModel", - "GraphQLScalar", - "GraphQLScalarModel", - "GraphQLType", - "GraphQLUnion", - "GraphQLUnionModel", - "ROOTS_NAMES", - "convert_graphql_name_to_python", - "convert_python_name_to_graphql", - "create_graphql_enum_model", - "deferred", - "get_description_node", - "get_value_from_node", - "get_value_node", - "graphql_enum", - "make_executable_schema", - "merge_root_nodes", - "object_field", - "sort_schema_document", -] diff --git a/ariadne_graphql_modules/next/executable_schema.py b/ariadne_graphql_modules/next/executable_schema.py deleted file mode 100644 index 79da59f..0000000 --- a/ariadne_graphql_modules/next/executable_schema.py +++ /dev/null @@ -1,243 +0,0 @@ -from enum import Enum -from typing import Any, Dict, List, Optional, Sequence, Type, Union - -from ariadne import ( - SchemaBindable, - SchemaDirectiveVisitor, - SchemaNameConverter, - convert_schema_names, - repair_schema_default_enum_values, - validate_schema_default_enum_values, -) -from graphql import ( - DocumentNode, - GraphQLSchema, - assert_valid_schema, - build_ast_schema, - parse, - concat_ast, -) - -from .base import GraphQLMetadata, GraphQLModel, GraphQLType -from .roots import ROOTS_NAMES, merge_root_nodes -from .sort import sort_schema_document - -SchemaType = Union[str, Enum, SchemaBindable, Type[GraphQLType], Type[Enum]] - - -def make_executable_schema( - *types: SchemaType, - directives: Optional[Dict[str, Type[SchemaDirectiveVisitor]]] = None, - convert_names_case: Union[bool, SchemaNameConverter] = False, - merge_roots: bool = True, -) -> GraphQLSchema: - metadata = GraphQLMetadata() - type_defs: List[str] = find_type_defs(types) - types_list: List[SchemaType] = flatten_types(types, metadata) - - assert_types_unique(types_list, merge_roots) - assert_types_not_abstract(types_list) - - schema_bindables: List[Union[SchemaBindable, GraphQLModel]] = [] - for type_def in types_list: - if isinstance(type_def, SchemaBindable): - schema_bindables.append(type_def) - elif isinstance(type_def, type) and issubclass(type_def, (GraphQLType, Enum)): - schema_bindables.append(metadata.get_graphql_model(type_def)) - - schema_models: List[GraphQLModel] = [ - type_def for type_def in schema_bindables if isinstance(type_def, GraphQLModel) - ] - - models_document: Optional[DocumentNode] = None - type_defs_document: Optional[DocumentNode] = None - - if schema_models: - models_document = DocumentNode( - definitions=tuple(schema_model.ast for schema_model in schema_models), - ) - - if type_defs: - type_defs_document = parse("\n".join(type_defs)) - - if models_document and type_defs_document: - document_node = concat_ast((models_document, type_defs_document)) - elif models_document: - document_node = models_document - elif type_defs_document: - document_node = type_defs_document - else: - raise ValueError( - "'make_executable_schema' was called without any GraphQL types." - ) - - if merge_roots: - document_node = merge_root_nodes(document_node) - - document_node = sort_schema_document(document_node) - schema = build_ast_schema(document_node) - - if directives: - SchemaDirectiveVisitor.visit_schema_directives(schema, directives) - - assert_valid_schema(schema) - validate_schema_default_enum_values(schema) - repair_schema_default_enum_values(schema) - - for schema_bindable in schema_bindables: - schema_bindable.bind_to_schema(schema) - - if convert_names_case: - convert_schema_names( - schema, - convert_names_case if callable(convert_names_case) else None, - ) - - return schema - - -def find_type_defs(types: Sequence[SchemaType]) -> List[str]: - type_defs: List[str] = [] - - for type_def in types: - if isinstance(type_def, str): - type_defs.append(type_def) - elif isinstance(type_def, list): - type_defs += find_type_defs(type_def) - - return type_defs - - -def flatten_types( - types: Sequence[SchemaType], - metadata: GraphQLMetadata, -) -> List[SchemaType]: - flat_schema_types_list: List[SchemaType] = flatten_schema_types( - types, metadata, dedupe=True - ) - - types_list: List[SchemaType] = [] - for type_def in flat_schema_types_list: - if isinstance(type_def, SchemaBindable): - types_list.append(type_def) - - elif isinstance(type_def, type) and issubclass(type_def, GraphQLType): - type_name = type_def.__name__ - - if getattr(type_def, "__abstract__", None): - raise ValueError( - f"Type '{type_name}' is an abstract type and can't be used " - "for schema creation." - ) - - types_list.append(type_def) - - elif isinstance(type_def, type) and issubclass(type_def, Enum): - types_list.append(type_def) - - elif isinstance(type_def, list): - types_list += find_type_defs(type_def) - - return types_list - - -def flatten_schema_types( - types: Sequence[Union[SchemaType, List[SchemaType]]], - metadata: GraphQLMetadata, - dedupe: bool, -) -> List[SchemaType]: - flat_list: List[SchemaType] = [] - checked_types: List[Type[GraphQLType]] = [] - - for type_def in types: - if isinstance(type_def, str): - continue - if isinstance(type_def, list): - flat_list += flatten_schema_types(type_def, metadata, dedupe=False) - elif isinstance(type_def, SchemaBindable): - flat_list.append(type_def) - elif isinstance(type_def, type) and issubclass(type_def, Enum): - flat_list.append(type_def) - elif isinstance(type_def, type) and issubclass(type_def, GraphQLType): - add_graphql_type_to_flat_list(flat_list, checked_types, type_def, metadata) - elif get_graphql_type_name(type_def): - flat_list.append(type_def) - - if not dedupe: - return flat_list - - unique_list: List[SchemaType] = [] - for type_def in flat_list: - if type_def not in unique_list: - unique_list.append(type_def) - - return unique_list - - -def add_graphql_type_to_flat_list( - flat_list: List[SchemaType], - checked_types: List[Type[GraphQLType]], - type_def: Type[GraphQLType], - metadata: GraphQLMetadata, -) -> None: - if type_def in checked_types: - return - - checked_types.append(type_def) - - for child_type in type_def.__get_graphql_types__(metadata): - flat_list.append(child_type) - - if issubclass(child_type, GraphQLType): - add_graphql_type_to_flat_list( - flat_list, checked_types, child_type, metadata - ) - - -def get_graphql_type_name(type_def: SchemaType) -> Optional[str]: - if isinstance(type_def, SchemaBindable): - return None - - if isinstance(type_def, type) and issubclass(type_def, Enum): - return type_def.__name__ - - if isinstance(type_def, type) and issubclass(type_def, GraphQLType): - return type_def.__get_graphql_name__() - - return None - - -def assert_types_unique(type_defs: List[SchemaType], merge_roots: bool): - types_names: Dict[str, Any] = {} - for type_def in type_defs: - type_name = get_graphql_type_name(type_def) - if not type_name: - continue - - if merge_roots and type_name in ROOTS_NAMES: - continue - - if type_name in types_names: - type_def_name = getattr(type_def, "__name__") or type_def - raise ValueError( - f"Types '{type_def_name}' and '{types_names[type_name]}' both define " - f"GraphQL type with name '{type_name}'." - ) - - types_names[type_name] = type_def - - -def assert_types_not_abstract(type_defs: List[SchemaType]): - for type_def in type_defs: - if isinstance(type_def, SchemaBindable): - continue - - if ( - isinstance(type_def, type) - and issubclass(type_def, GraphQLType) - and getattr(type_def, "__abstract__", None) - ): - raise ValueError( - f"Type '{type_def.__name__}' is an abstract type and can't be used " - "for schema creation." - ) diff --git a/ariadne_graphql_modules/next/graphql_enum_type/__init__.py b/ariadne_graphql_modules/next/graphql_enum_type/__init__.py deleted file mode 100644 index a4c8666..0000000 --- a/ariadne_graphql_modules/next/graphql_enum_type/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .enum_type import GraphQLEnum, graphql_enum, create_graphql_enum_model -from .enum_model import GraphQLEnumModel - -__all__ = [ - "GraphQLEnum", - "GraphQLEnumModel", - "create_graphql_enum_model", - "graphql_enum", -] diff --git a/ariadne_graphql_modules/next/graphql_input/__init__.py b/ariadne_graphql_modules/next/graphql_input/__init__.py deleted file mode 100644 index 5d1e0b5..0000000 --- a/ariadne_graphql_modules/next/graphql_input/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .input_type import GraphQLInput -from .input_model import GraphQLInputModel - - -__all__ = [ - "GraphQLInput", - "GraphQLInputModel", -] diff --git a/ariadne_graphql_modules/next/graphql_interface/__init__.py b/ariadne_graphql_modules/next/graphql_interface/__init__.py deleted file mode 100644 index 7ccd6d6..0000000 --- a/ariadne_graphql_modules/next/graphql_interface/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .interface_type import GraphQLInterface -from .interface_model import GraphQLInterfaceModel - - -__all__ = [ - "GraphQLInterface", - "GraphQLInterfaceModel", -] diff --git a/ariadne_graphql_modules/next/graphql_interface/interface_type.py b/ariadne_graphql_modules/next/graphql_interface/interface_type.py deleted file mode 100644 index 1a7832b..0000000 --- a/ariadne_graphql_modules/next/graphql_interface/interface_type.py +++ /dev/null @@ -1,175 +0,0 @@ -from typing import Any, Dict, List, cast - -from ariadne.types import Resolver -from graphql import ( - FieldDefinitionNode, - InputValueDefinitionNode, - InterfaceTypeDefinitionNode, - NameNode, - NamedTypeNode, - StringValueNode, -) - -from ...utils import parse_definition -from ..base import GraphQLMetadata -from ..description import get_description_node -from ..graphql_object import GraphQLObject, GraphQLObjectResolver -from ..graphql_object.object_type import get_graphql_object_data -from ..graphql_object.utils import ( - get_field_args_from_resolver, - get_field_args_out_names, - get_field_node_from_obj_field, - update_field_args_options, -) -from ..value import get_value_node -from .interface_model import GraphQLInterfaceModel - - -class GraphQLInterface(GraphQLObject): - __abstract__: bool = True - __valid_type__ = InterfaceTypeDefinitionNode - - @classmethod - def __get_graphql_model_with_schema__(cls) -> "GraphQLInterfaceModel": - definition = cast( - InterfaceTypeDefinitionNode, - parse_definition(InterfaceTypeDefinitionNode, cls.__schema__), - ) - - descriptions: Dict[str, StringValueNode] = {} - args_descriptions: Dict[str, Dict[str, StringValueNode]] = {} - args_defaults: Dict[str, Dict[str, Any]] = {} - resolvers: Dict[str, Resolver] = {} - out_names: Dict[str, Dict[str, str]] = {} - - for attr_name in dir(cls): - cls_attr = getattr(cls, attr_name) - if isinstance(cls_attr, GraphQLObjectResolver): - resolvers[cls_attr.field] = cls_attr.resolver - description_node = get_description_node(cls_attr.description) - if description_node: - descriptions[cls_attr.field] = description_node - - field_args = get_field_args_from_resolver(cls_attr.resolver) - if field_args: - args_descriptions[cls_attr.field] = {} - args_defaults[cls_attr.field] = {} - - final_args = update_field_args_options(field_args, cls_attr.args) - - for arg_name, arg_options in final_args.items(): - arg_description = get_description_node(arg_options.description) - if arg_description: - args_descriptions[cls_attr.field][ - arg_name - ] = arg_description - - arg_default = arg_options.default_value - if arg_default is not None: - args_defaults[cls_attr.field][arg_name] = get_value_node( - arg_default - ) - - fields: List[FieldDefinitionNode] = [] - for field in definition.fields: - field_args_descriptions = args_descriptions.get(field.name.value, {}) - field_args_defaults = args_defaults.get(field.name.value, {}) - - args: List[InputValueDefinitionNode] = [] - for arg in field.arguments: - arg_name = arg.name.value - args.append( - InputValueDefinitionNode( - description=( - arg.description or field_args_descriptions.get(arg_name) - ), - name=arg.name, - directives=arg.directives, - type=arg.type, - default_value=( - arg.default_value or field_args_defaults.get(arg_name) - ), - ) - ) - - fields.append( - FieldDefinitionNode( - name=field.name, - description=( - field.description or descriptions.get(field.name.value) - ), - directives=field.directives, - arguments=tuple(args), - type=field.type, - ) - ) - - return GraphQLInterfaceModel( - name=definition.name.value, - ast_type=InterfaceTypeDefinitionNode, - ast=InterfaceTypeDefinitionNode( - name=NameNode(value=definition.name.value), - fields=tuple(fields), - interfaces=definition.interfaces, - ), - resolve_type=cls.resolve_type, - resolvers=resolvers, - aliases=getattr(cls, "__aliases__", {}), - out_names=out_names, - ) - - @classmethod - def __get_graphql_model_without_schema__( - cls, metadata: GraphQLMetadata, name: str - ) -> "GraphQLInterfaceModel": - type_data = get_graphql_object_data(metadata, cls) - type_aliases = getattr(cls, "__aliases__", None) or {} - - fields_ast: List[FieldDefinitionNode] = [] - resolvers: Dict[str, Resolver] = {} - aliases: Dict[str, str] = {} - out_names: Dict[str, Dict[str, str]] = {} - - for attr_name, field in type_data.fields.items(): - fields_ast.append(get_field_node_from_obj_field(cls, metadata, field)) - - if attr_name in type_aliases and field.name: - aliases[field.name] = type_aliases[attr_name] - elif field.name and attr_name != field.name and not field.resolver: - aliases[field.name] = attr_name - - if field.resolver and field.name: - resolvers[field.name] = field.resolver - - if field.args and field.name: - out_names[field.name] = get_field_args_out_names(field.args) - - interfaces_ast: List[NamedTypeNode] = [] - for interface_name in type_data.interfaces: - interfaces_ast.append(NamedTypeNode(name=NameNode(value=interface_name))) - - return GraphQLInterfaceModel( - name=name, - ast_type=InterfaceTypeDefinitionNode, - ast=InterfaceTypeDefinitionNode( - name=NameNode(value=name), - description=get_description_node( - getattr(cls, "__description__", None), - ), - fields=tuple(fields_ast), - interfaces=tuple(interfaces_ast), - ), - resolve_type=cls.resolve_type, - resolvers=resolvers, - aliases=aliases, - out_names=out_names, - ) - - @staticmethod - def resolve_type(obj: Any, *_) -> str: - if isinstance(obj, GraphQLObject): - return obj.__get_graphql_name__() - - raise ValueError( - f"Cannot resolve GraphQL type {obj} for object of type '{type(obj).__name__}'." - ) diff --git a/ariadne_graphql_modules/next/graphql_scalar/__init__.py b/ariadne_graphql_modules/next/graphql_scalar/__init__.py deleted file mode 100644 index 1c1d0d2..0000000 --- a/ariadne_graphql_modules/next/graphql_scalar/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .scalar_type import GraphQLScalar -from .scalar_model import GraphQLScalarModel - - -__all__ = [ - "GraphQLScalar", - "GraphQLScalarModel", -] diff --git a/ariadne_graphql_modules/next/graphql_subscription/__init__.py b/ariadne_graphql_modules/next/graphql_subscription/__init__.py deleted file mode 100644 index 962d24e..0000000 --- a/ariadne_graphql_modules/next/graphql_subscription/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .subscription_type import GraphQLSubscription -from .subscription_model import GraphQLSubscriptionModel - - -__all__ = [ - "GraphQLSubscription", - "GraphQLSubscriptionModel", -] diff --git a/ariadne_graphql_modules/next/graphql_union/__init__.py b/ariadne_graphql_modules/next/graphql_union/__init__.py deleted file mode 100644 index 7e9462d..0000000 --- a/ariadne_graphql_modules/next/graphql_union/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .union_type import GraphQLUnion -from .union_model import GraphQLUnionModel - - -__all__ = [ - "GraphQLUnion", - "GraphQLUnionModel", -] diff --git a/ariadne_graphql_modules/next/graphql_object/__init__.py b/ariadne_graphql_modules/object_type/__init__.py similarity index 77% rename from ariadne_graphql_modules/next/graphql_object/__init__.py rename to ariadne_graphql_modules/object_type/__init__.py index a2b2c67..03db715 100644 --- a/ariadne_graphql_modules/next/graphql_object/__init__.py +++ b/ariadne_graphql_modules/object_type/__init__.py @@ -1,13 +1,13 @@ -from .object_field import ( +from ..base_object_type.graphql_field import ( object_field, GraphQLObjectResolver, GraphQLObjectSource, object_subscriber, GraphQLObjectFieldArg, ) -from .object_type import GraphQLObject, get_graphql_object_data -from .object_model import GraphQLObjectModel -from .utils import ( +from .graphql_type import GraphQLObject +from .models import GraphQLObjectModel +from ..base_object_type.utils import ( get_field_args_from_resolver, get_field_args_out_names, get_field_node_from_obj_field, @@ -24,7 +24,6 @@ "get_field_node_from_obj_field", "update_field_args_options", "GraphQLObjectResolver", - "get_graphql_object_data", "GraphQLObjectSource", "object_subscriber", "get_field_args_from_subscriber", diff --git a/ariadne_graphql_modules/object_type/graphql_type.py b/ariadne_graphql_modules/object_type/graphql_type.py new file mode 100644 index 0000000..89e0227 --- /dev/null +++ b/ariadne_graphql_modules/object_type/graphql_type.py @@ -0,0 +1,148 @@ +from typing import ( + Dict, + List, + Optional, + Tuple, + cast, +) + +from ariadne.types import Resolver +from graphql import ( + FieldDefinitionNode, + NameNode, + NamedTypeNode, + ObjectTypeDefinitionNode, +) + +from ..types import GraphQLClassType + +from ..base_object_type import ( + GraphQLFieldData, + GraphQLBaseObject, + GraphQLObjectData, + validate_object_type_with_schema, + validate_object_type_without_schema, +) +from .models import GraphQLObjectModel + + +from ..utils import parse_definition +from ..base import GraphQLMetadata, GraphQLModel +from ..description import get_description_node + + +class GraphQLObject(GraphQLBaseObject): + __valid_type__ = ObjectTypeDefinitionNode + __graphql_type__ = GraphQLClassType.OBJECT + __abstract__ = True + __description__: Optional[str] = None + + def __init_subclass__(cls) -> None: + super().__init_subclass__() + + if cls.__dict__.get("__abstract__"): + return + + cls.__abstract__ = False + + if cls.__dict__.get("__schema__"): + valid_type = getattr(cls, "__valid_type__", ObjectTypeDefinitionNode) + cls.__kwargs__ = validate_object_type_with_schema(cls, valid_type) + else: + cls.__kwargs__ = validate_object_type_without_schema(cls) + + @classmethod + def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": + definition = cast( + ObjectTypeDefinitionNode, + parse_definition(ObjectTypeDefinitionNode, cls.__schema__), + ) + + resolvers: Dict[str, Resolver] = {} + fields: Tuple[FieldDefinitionNode, ...] = tuple() + fields, resolvers = cls._create_fields_and_resolvers_with_schema( + definition.fields + ) + + return GraphQLObjectModel( + name=definition.name.value, + ast_type=ObjectTypeDefinitionNode, + ast=ObjectTypeDefinitionNode( + name=NameNode(value=definition.name.value), + fields=tuple(fields), + interfaces=definition.interfaces, + ), + resolvers=resolvers, + aliases=getattr(cls, "__aliases__", {}), + out_names={}, + ) + + @classmethod + def __get_graphql_model_without_schema__( + cls, metadata: GraphQLMetadata, name: str + ) -> "GraphQLModel": + type_data = cls.get_graphql_object_data(metadata) + type_aliases = getattr(cls, "__aliases__", {}) + + fields_ast: List[FieldDefinitionNode] = [] + resolvers: Dict[str, Resolver] = {} + aliases: Dict[str, str] = {} + out_names: Dict[str, Dict[str, str]] = {} + + fields_ast, resolvers, aliases, out_names = cls._process_graphql_fields( + metadata, type_data, type_aliases + ) + + interfaces_ast: List[NamedTypeNode] = [] + for interface_name in type_data.interfaces: + interfaces_ast.append(NamedTypeNode(name=NameNode(value=interface_name))) + + return GraphQLObjectModel( + name=name, + ast_type=ObjectTypeDefinitionNode, + ast=ObjectTypeDefinitionNode( + name=NameNode(value=name), + description=get_description_node( + getattr(cls, "__description__", None), + ), + fields=tuple(fields_ast), + interfaces=tuple(interfaces_ast), + ), + resolvers=resolvers, + aliases=aliases, + out_names=out_names, + ) + + @classmethod + def _collect_inherited_objects(cls): + return [ + inherited_obj + for inherited_obj in cls.__mro__[1:] + if getattr(inherited_obj, "__graphql_type__", None) + in (GraphQLClassType.INTERFACE, GraphQLClassType.OBJECT) + and not getattr(inherited_obj, "__abstract__", True) + ] + + @classmethod + def create_graphql_object_data_without_schema(cls) -> GraphQLObjectData: + fields_data = GraphQLFieldData() + inherited_objects = list(reversed(cls._collect_inherited_objects())) + + for inherited_obj in inherited_objects: + fields_data.type_hints.update(inherited_obj.__annotations__) + fields_data.aliases.update(getattr(inherited_obj, "__aliases__", {})) + + cls._process_type_hints_and_aliases(fields_data) + + for inherited_obj in inherited_objects: + cls._process_class_attributes(inherited_obj, fields_data) + cls._process_class_attributes(cls, fields_data) + return GraphQLObjectData( + fields=cls._build_fields(fields_data=fields_data), + interfaces=[ + interface.__name__ + for interface in inherited_objects + if getattr(interface, "__graphql_type__", None) + == GraphQLClassType.INTERFACE + ], + ) diff --git a/ariadne_graphql_modules/next/graphql_object/object_model.py b/ariadne_graphql_modules/object_type/models.py similarity index 94% rename from ariadne_graphql_modules/next/graphql_object/object_model.py rename to ariadne_graphql_modules/object_type/models.py index 7a75065..1241188 100644 --- a/ariadne_graphql_modules/next/graphql_object/object_model.py +++ b/ariadne_graphql_modules/object_type/models.py @@ -5,7 +5,7 @@ from ariadne.types import Resolver from graphql import GraphQLField, GraphQLObjectType, GraphQLSchema -from ariadne_graphql_modules.next.base import GraphQLModel +from ..base import GraphQLModel @dataclass(frozen=True) diff --git a/ariadne_graphql_modules/next/roots.py b/ariadne_graphql_modules/roots.py similarity index 100% rename from ariadne_graphql_modules/next/roots.py rename to ariadne_graphql_modules/roots.py diff --git a/ariadne_graphql_modules/scalar_type/__init__.py b/ariadne_graphql_modules/scalar_type/__init__.py new file mode 100644 index 0000000..82909c7 --- /dev/null +++ b/ariadne_graphql_modules/scalar_type/__init__.py @@ -0,0 +1,8 @@ +from .graphql_type import GraphQLScalar +from .models import GraphQLScalarModel + + +__all__ = [ + "GraphQLScalar", + "GraphQLScalarModel", +] diff --git a/ariadne_graphql_modules/next/graphql_scalar/scalar_type.py b/ariadne_graphql_modules/scalar_type/graphql_type.py similarity index 97% rename from ariadne_graphql_modules/next/graphql_scalar/scalar_type.py rename to ariadne_graphql_modules/scalar_type/graphql_type.py index 75436c8..60f27f3 100644 --- a/ariadne_graphql_modules/next/graphql_scalar/scalar_type.py +++ b/ariadne_graphql_modules/scalar_type/graphql_type.py @@ -8,9 +8,9 @@ ) from ..description import get_description_node -from .scalar_model import GraphQLScalarModel +from .models import GraphQLScalarModel -from ...utils import parse_definition +from ..utils import parse_definition from .validators import validate_scalar_type_with_schema diff --git a/ariadne_graphql_modules/next/graphql_scalar/scalar_model.py b/ariadne_graphql_modules/scalar_type/models.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_scalar/scalar_model.py rename to ariadne_graphql_modules/scalar_type/models.py diff --git a/ariadne_graphql_modules/next/graphql_scalar/validators.py b/ariadne_graphql_modules/scalar_type/validators.py similarity index 89% rename from ariadne_graphql_modules/next/graphql_scalar/validators.py rename to ariadne_graphql_modules/scalar_type/validators.py index 374c482..ddf6512 100644 --- a/ariadne_graphql_modules/next/graphql_scalar/validators.py +++ b/ariadne_graphql_modules/scalar_type/validators.py @@ -4,10 +4,10 @@ from ..validators import validate_description, validate_name -from ...utils import parse_definition +from ..utils import parse_definition if TYPE_CHECKING: - from .scalar_type import GraphQLScalar + from .graphql_type import GraphQLScalar def validate_scalar_type_with_schema(cls: Type["GraphQLScalar"]): diff --git a/ariadne_graphql_modules/next/sort.py b/ariadne_graphql_modules/sort.py similarity index 100% rename from ariadne_graphql_modules/next/sort.py rename to ariadne_graphql_modules/sort.py diff --git a/ariadne_graphql_modules/subscription_type/__init__.py b/ariadne_graphql_modules/subscription_type/__init__.py new file mode 100644 index 0000000..c271e40 --- /dev/null +++ b/ariadne_graphql_modules/subscription_type/__init__.py @@ -0,0 +1,8 @@ +from .graphql_type import GraphQLSubscription +from .models import GraphQLSubscriptionModel + + +__all__ = [ + "GraphQLSubscription", + "GraphQLSubscriptionModel", +] diff --git a/ariadne_graphql_modules/next/graphql_subscription/subscription_type.py b/ariadne_graphql_modules/subscription_type/graphql_type.py similarity index 78% rename from ariadne_graphql_modules/next/graphql_subscription/subscription_type.py rename to ariadne_graphql_modules/subscription_type/graphql_type.py index 41aef33..90fd7e7 100644 --- a/ariadne_graphql_modules/next/graphql_subscription/subscription_type.py +++ b/ariadne_graphql_modules/subscription_type/graphql_type.py @@ -11,12 +11,22 @@ from ariadne.types import Resolver, Subscriber +from ..base_object_type import ( + GraphQLFieldData, + GraphQLObjectData, + validate_object_type_with_schema, + validate_object_type_without_schema, +) + +from ..types import GraphQLClassType + +from ..base_object_type import GraphQLBaseObject -from ...utils import parse_definition + +from ..utils import parse_definition from ..base import GraphQLMetadata, GraphQLModel from ..description import get_description_node -from ..graphql_object import ( - GraphQLObject, +from ..object_type import ( GraphQLObjectResolver, GraphQLObjectSource, GraphQLObjectFieldArg, @@ -24,17 +34,32 @@ get_field_args_from_subscriber, get_field_args_out_names, get_field_node_from_obj_field, - get_graphql_object_data, object_subscriber, update_field_args_options, ) from ..value import get_value_node -from .subscription_model import GraphQLSubscriptionModel +from .models import GraphQLSubscriptionModel -class GraphQLSubscription(GraphQLObject): - __abstract__: bool = True +class GraphQLSubscription(GraphQLBaseObject): __valid_type__ = ObjectTypeDefinitionNode + __graphql_type__ = GraphQLClassType.SUBSCRIPTION + __abstract__: bool = True + __description__: Optional[str] = None + + def __init_subclass__(cls) -> None: + super().__init_subclass__() + + if cls.__dict__.get("__abstract__"): + return + + cls.__abstract__ = False + + if cls.__dict__.get("__schema__"): + valid_type = getattr(cls, "__valid_type__", ObjectTypeDefinitionNode) + cls.__kwargs__ = validate_object_type_with_schema(cls, valid_type) + else: + cls.__kwargs__ = validate_object_type_without_schema(cls) @classmethod def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": @@ -48,7 +73,6 @@ def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": args_defaults: Dict[str, Dict[str, Any]] = {} resolvers: Dict[str, Resolver] = {} subscribers: Dict[str, Subscriber] = {} - out_names: Dict[str, Dict[str, str]] = {} for attr_name in dir(cls): cls_attr = getattr(cls, attr_name) @@ -148,14 +172,14 @@ def __get_graphql_model_with_schema__(cls) -> "GraphQLModel": resolvers=resolvers, subscribers=subscribers, aliases=getattr(cls, "__aliases__", {}), - out_names=out_names, + out_names={}, ) @classmethod def __get_graphql_model_without_schema__( cls, metadata: GraphQLMetadata, name: str ) -> "GraphQLModel": - type_data = get_graphql_object_data(metadata, cls) + type_data = cls.get_graphql_object_data(metadata) type_aliases = getattr(cls, "__aliases__", None) or {} fields_ast: List[FieldDefinitionNode] = [] @@ -215,3 +239,33 @@ def source( graphql_type=graphql_type, description=description, ) + + @classmethod + def _collect_inherited_objects(cls): + return [ + inherited_obj + for inherited_obj in cls.__mro__[1:] + if getattr(inherited_obj, "__graphql_type__", None) + == GraphQLClassType.SUBSCRIPTION + and not getattr(inherited_obj, "__abstract__", True) + ] + + @classmethod + def create_graphql_object_data_without_schema(cls) -> GraphQLObjectData: + fields_data = GraphQLFieldData() + inherited_objects = list(reversed(cls._collect_inherited_objects())) + + for inherited_obj in inherited_objects: + fields_data.type_hints.update(inherited_obj.__annotations__) + fields_data.aliases.update(getattr(inherited_obj, "__aliases__", {})) + + cls._process_type_hints_and_aliases(fields_data) + + for inherited_obj in inherited_objects: + cls._process_class_attributes(inherited_obj, fields_data) + cls._process_class_attributes(cls, fields_data) + + return GraphQLObjectData( + fields=cls._build_fields(fields_data=fields_data), + interfaces=[], + ) diff --git a/ariadne_graphql_modules/next/graphql_subscription/subscription_model.py b/ariadne_graphql_modules/subscription_type/models.py similarity index 95% rename from ariadne_graphql_modules/next/graphql_subscription/subscription_model.py rename to ariadne_graphql_modules/subscription_type/models.py index 95bff2d..d32aa1b 100644 --- a/ariadne_graphql_modules/next/graphql_subscription/subscription_model.py +++ b/ariadne_graphql_modules/subscription_type/models.py @@ -5,7 +5,7 @@ from ariadne.types import Resolver, Subscriber from graphql import GraphQLField, GraphQLObjectType, GraphQLSchema -from ariadne_graphql_modules.next.base import GraphQLModel +from ..base import GraphQLModel @dataclass(frozen=True) diff --git a/ariadne_graphql_modules/types.py b/ariadne_graphql_modules/types.py index 9c2173b..13f8880 100644 --- a/ariadne_graphql_modules/types.py +++ b/ariadne_graphql_modules/types.py @@ -1,5 +1,6 @@ from typing import Dict, Type +from enum import Enum from graphql import ( DefinitionNode, FieldDefinitionNode, @@ -9,3 +10,10 @@ FieldsDict = Dict[str, FieldDefinitionNode] InputFieldsDict = Dict[str, InputValueDefinitionNode] RequirementsDict = Dict[str, Type[DefinitionNode]] + + +class GraphQLClassType(Enum): + BASE = "base" + OBJECT = "object" + INTERFACE = "interface" + SUBSCRIPTION = "subscription" diff --git a/ariadne_graphql_modules/next/typing.py b/ariadne_graphql_modules/typing.py similarity index 100% rename from ariadne_graphql_modules/next/typing.py rename to ariadne_graphql_modules/typing.py diff --git a/ariadne_graphql_modules/union_type/__init__.py b/ariadne_graphql_modules/union_type/__init__.py new file mode 100644 index 0000000..d6ebe06 --- /dev/null +++ b/ariadne_graphql_modules/union_type/__init__.py @@ -0,0 +1,8 @@ +from .graphql_type import GraphQLUnion +from .models import GraphQLUnionModel + + +__all__ = [ + "GraphQLUnion", + "GraphQLUnionModel", +] diff --git a/ariadne_graphql_modules/next/graphql_union/union_type.py b/ariadne_graphql_modules/union_type/graphql_type.py similarity index 95% rename from ariadne_graphql_modules/next/graphql_union/union_type.py rename to ariadne_graphql_modules/union_type/graphql_type.py index d74c796..154c8c8 100644 --- a/ariadne_graphql_modules/next/graphql_union/union_type.py +++ b/ariadne_graphql_modules/union_type/graphql_type.py @@ -3,13 +3,13 @@ from graphql import NameNode, NamedTypeNode, UnionTypeDefinitionNode from ..base import GraphQLMetadata, GraphQLModel, GraphQLType from ..description import get_description_node -from ..graphql_object.object_type import GraphQLObject -from .union_model import GraphQLUnionModel +from ..object_type.graphql_type import GraphQLObject +from .models import GraphQLUnionModel from .validators import ( validate_union_type, validate_union_type_with_schema, ) -from ...utils import parse_definition +from ..utils import parse_definition class GraphQLUnion(GraphQLType): diff --git a/ariadne_graphql_modules/next/graphql_union/union_model.py b/ariadne_graphql_modules/union_type/models.py similarity index 100% rename from ariadne_graphql_modules/next/graphql_union/union_model.py rename to ariadne_graphql_modules/union_type/models.py diff --git a/ariadne_graphql_modules/next/graphql_union/validators.py b/ariadne_graphql_modules/union_type/validators.py similarity index 96% rename from ariadne_graphql_modules/next/graphql_union/validators.py rename to ariadne_graphql_modules/union_type/validators.py index 3d7f280..d22aece 100644 --- a/ariadne_graphql_modules/next/graphql_union/validators.py +++ b/ariadne_graphql_modules/union_type/validators.py @@ -4,10 +4,10 @@ from ..validators import validate_description, validate_name -from ...utils import parse_definition +from ..utils import parse_definition if TYPE_CHECKING: - from .union_type import GraphQLUnion + from .graphql_type import GraphQLUnion def validate_union_type(cls: Type["GraphQLUnion"]) -> None: diff --git a/ariadne_graphql_modules/v1/__init__.py b/ariadne_graphql_modules/v1/__init__.py new file mode 100644 index 0000000..d6406a3 --- /dev/null +++ b/ariadne_graphql_modules/v1/__init__.py @@ -0,0 +1,38 @@ +from ariadne import gql + +from .bases import BaseType, BindableType, DeferredType, DefinitionType +from .collection_type import CollectionType +from .convert_case import convert_case +from .directive_type import DirectiveType +from .enum_type import EnumType +from .executable_schema import make_executable_schema +from .input_type import InputType +from .interface_type import InterfaceType +from .mutation_type import MutationType +from .object_type import ObjectType +from .scalar_type import ScalarType +from .subscription_type import SubscriptionType +from .union_type import UnionType +from ..utils import create_alias_resolver, parse_definition + +__all__ = [ + "BaseType", + "BindableType", + "CollectionType", + "DeferredType", + "DefinitionType", + "DirectiveType", + "EnumType", + "InputType", + "InterfaceType", + "MutationType", + "ObjectType", + "ScalarType", + "SubscriptionType", + "UnionType", + "convert_case", + "create_alias_resolver", + "gql", + "make_executable_schema", + "parse_definition", +] diff --git a/ariadne_graphql_modules/bases.py b/ariadne_graphql_modules/v1/bases.py similarity index 98% rename from ariadne_graphql_modules/bases.py rename to ariadne_graphql_modules/v1/bases.py index 2ff42e2..cfc983c 100644 --- a/ariadne_graphql_modules/bases.py +++ b/ariadne_graphql_modules/v1/bases.py @@ -8,7 +8,7 @@ ) from .dependencies import Dependencies -from .types import RequirementsDict +from ..types import RequirementsDict __all__ = ["BaseType", "BindableType", "DeferredType", "DefinitionType"] diff --git a/ariadne_graphql_modules/collection_type.py b/ariadne_graphql_modules/v1/collection_type.py similarity index 100% rename from ariadne_graphql_modules/collection_type.py rename to ariadne_graphql_modules/v1/collection_type.py diff --git a/ariadne_graphql_modules/convert_case.py b/ariadne_graphql_modules/v1/convert_case.py similarity index 97% rename from ariadne_graphql_modules/convert_case.py rename to ariadne_graphql_modules/v1/convert_case.py index d0c7b18..f44e878 100644 --- a/ariadne_graphql_modules/convert_case.py +++ b/ariadne_graphql_modules/v1/convert_case.py @@ -4,7 +4,7 @@ from ariadne import convert_camel_case_to_snake from graphql import FieldDefinitionNode -from .types import FieldsDict, InputFieldsDict +from ..types import FieldsDict, InputFieldsDict def convert_case( diff --git a/ariadne_graphql_modules/dependencies.py b/ariadne_graphql_modules/v1/dependencies.py similarity index 99% rename from ariadne_graphql_modules/dependencies.py rename to ariadne_graphql_modules/v1/dependencies.py index f3b436d..cd04c06 100644 --- a/ariadne_graphql_modules/dependencies.py +++ b/ariadne_graphql_modules/v1/dependencies.py @@ -15,7 +15,7 @@ UnionTypeExtensionNode, ) -from .utils import unwrap_type_node +from ..utils import unwrap_type_node GRAPHQL_TYPES = ("ID", "Int", "String", "Boolean", "Float") diff --git a/ariadne_graphql_modules/directive_type.py b/ariadne_graphql_modules/v1/directive_type.py similarity index 97% rename from ariadne_graphql_modules/directive_type.py rename to ariadne_graphql_modules/v1/directive_type.py index d6e2706..14d9817 100644 --- a/ariadne_graphql_modules/directive_type.py +++ b/ariadne_graphql_modules/v1/directive_type.py @@ -7,7 +7,7 @@ ) from .bases import DefinitionType -from .utils import parse_definition +from ..utils import parse_definition class DirectiveType(DefinitionType): diff --git a/ariadne_graphql_modules/enum_type.py b/ariadne_graphql_modules/v1/enum_type.py similarity index 98% rename from ariadne_graphql_modules/enum_type.py rename to ariadne_graphql_modules/v1/enum_type.py index c555b33..66236d4 100644 --- a/ariadne_graphql_modules/enum_type.py +++ b/ariadne_graphql_modules/v1/enum_type.py @@ -10,8 +10,8 @@ ) from .bases import BindableType -from .types import RequirementsDict -from .utils import parse_definition +from ..types import RequirementsDict +from ..utils import parse_definition EnumNodeType = Union[EnumTypeDefinitionNode, EnumTypeExtensionNode] diff --git a/ariadne_graphql_modules/v1/executable_schema.py b/ariadne_graphql_modules/v1/executable_schema.py new file mode 100644 index 0000000..1914742 --- /dev/null +++ b/ariadne_graphql_modules/v1/executable_schema.py @@ -0,0 +1,236 @@ +from typing import ( + Dict, + List, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) + +from ariadne import ( + SchemaBindable, + SchemaDirectiveVisitor, + repair_schema_default_enum_values, + validate_schema_default_enum_values, +) +from graphql import ( + ConstDirectiveNode, + DocumentNode, + FieldDefinitionNode, + GraphQLSchema, + NamedTypeNode, + ObjectTypeDefinitionNode, + TypeDefinitionNode, + assert_valid_schema, + build_ast_schema, + concat_ast, + parse, +) +from graphql.language import ast + +from .bases import BaseType, BindableType, DeferredType, DefinitionType +from .enum_type import EnumType + +ROOT_TYPES = ["Query", "Mutation", "Subscription"] + + +def make_executable_schema( + *args: Union[Type[BaseType], SchemaBindable, str], + merge_roots: bool = True, + extra_directives: Optional[Dict[str, Type[SchemaDirectiveVisitor]]] = None, +): + all_types = get_all_types(args) + extra_defs = parse_extra_sdl(args) + extra_bindables: List[SchemaBindable] = [ + arg for arg in args if isinstance(arg, SchemaBindable) + ] + + type_defs: List[Type[DefinitionType]] = [] + for type_ in all_types: + if issubclass(type_, DefinitionType): + type_defs.append(type_) + + validate_no_missing_definitions(all_types, type_defs, extra_defs) + + schema = build_schema(type_defs, extra_defs, merge_roots) + + if extra_bindables: + for bindable in extra_bindables: + bindable.bind_to_schema(schema) + + if extra_directives: + SchemaDirectiveVisitor.visit_schema_directives(schema, extra_directives) + + assert_valid_schema(schema) + validate_schema_default_enum_values(schema) + repair_schema_default_enum_values(schema) + + add_directives_to_schema(schema, type_defs) + + return schema + + +def get_all_types( + args: Sequence[Union[Type[BaseType], SchemaBindable, str]] +) -> List[Type[BaseType]]: + all_types: List[Type[BaseType]] = [] + for arg in args: + if isinstance(arg, (str, SchemaBindable)): + continue # Skip args of unsupported types + + for child_type in arg.__get_types__(): + if child_type not in all_types: + all_types.append(child_type) + return all_types + + +def parse_extra_sdl( + args: Sequence[Union[Type[BaseType], SchemaBindable, str]] +) -> List[TypeDefinitionNode]: + sdl_strings: List[str] = [cast(str, arg) for arg in args if isinstance(arg, str)] + if not sdl_strings: + return [] + + extra_sdl = "\n\n".join(sdl_strings) + return cast( + List[TypeDefinitionNode], + list(parse(extra_sdl).definitions), + ) + + +def validate_no_missing_definitions( + all_types: List[Type[BaseType]], + type_defs: List[Type[DefinitionType]], + extra_defs: List[TypeDefinitionNode], +): + deferred_names: List[str] = [] + for type_ in all_types: + if isinstance(type_, DeferredType): + deferred_names.append(type_.graphql_name) + + real_names = [type_.graphql_name for type_ in type_defs] + real_names += [definition.name.value for definition in extra_defs] + + missing_names = set(deferred_names) - set(real_names) + if missing_names: + raise ValueError( + "Following types are defined as deferred and are missing " + f"from schema: {', '.join(missing_names)}" + ) + + +def build_schema( + type_defs: List[Type[DefinitionType]], + extra_defs: List[TypeDefinitionNode], + merge_roots: bool = True, +) -> GraphQLSchema: + schema_definitions: List[ast.DocumentNode] = [] + if merge_roots: + schema_definitions.append(build_root_schema(type_defs, extra_defs)) + for type_ in type_defs: + if type_.graphql_name not in ROOT_TYPES or not merge_roots: + schema_definitions.append(parse(type_.__schema__)) + for extra_type_def in extra_defs: + if extra_type_def.name.value not in ROOT_TYPES or not merge_roots: + schema_definitions.append(DocumentNode(definitions=(extra_type_def,))) + + ast_document = concat_ast(schema_definitions) + schema = build_ast_schema(ast_document) + + for type_ in type_defs: + if issubclass(type_, BindableType): + type_.__bind_to_schema__(schema) + + return schema + + +RootTypeDef = Tuple[str, DocumentNode] + + +def build_root_schema( + type_defs: List[Type[DefinitionType]], + extra_defs: List[TypeDefinitionNode], +) -> DocumentNode: + root_types: Dict[str, List[RootTypeDef]] = { + "Query": [], + "Mutation": [], + "Subscription": [], + } + + for type_def in type_defs: + if type_def.graphql_name in root_types: + root_types[type_def.graphql_name].append( + (type_def.__name__, parse(type_def.__schema__)) + ) + + for extra_type_def in extra_defs: + if extra_type_def.name.value in root_types: + root_types[extra_type_def.name.value].append( + ("extra_sdl", DocumentNode(definitions=(extra_type_def,))) + ) + + schema: List[DocumentNode] = [] + for root_name, root_type_defs in root_types.items(): + if len(root_type_defs) == 1: + schema.append(root_type_defs[0][1]) + elif root_type_defs: + schema.append(merge_root_types(root_name, root_type_defs)) + + return concat_ast(schema) + + +def merge_root_types(root_name: str, type_defs: List[RootTypeDef]) -> DocumentNode: + interfaces: List[NamedTypeNode] = [] + directives: List[ConstDirectiveNode] = [] + fields: Dict[str, Tuple[str, FieldDefinitionNode]] = {} + + for type_source, type_def in type_defs: + type_definition = cast(ObjectTypeDefinitionNode, type_def.definitions[0]) + interfaces.extend(type_definition.interfaces) + directives.extend(type_definition.directives) + + for field_def in type_definition.fields: + field_name = field_def.name.value + if field_name in fields: + other_type_source = fields[field_name][0] + raise ValueError( + f"Multiple {root_name} types are defining same field " + f"'{field_name}': {other_type_source}, {type_source}" + ) + + fields[field_name] = (type_source, field_def) + + merged_definition = ast.ObjectTypeDefinitionNode() + merged_definition.name = ast.NameNode() + merged_definition.name.value = root_name + merged_definition.interfaces = tuple(interfaces) + merged_definition.directives = tuple(directives) + merged_definition.fields = tuple( + fields[field_name][1] for field_name in sorted(fields) + ) + + merged_document = DocumentNode() + merged_document.definitions = (merged_definition,) + + return merged_document + + +def add_directives_to_schema( + schema: GraphQLSchema, type_defs: List[Type[DefinitionType]] +): + directives: Dict[str, Type[SchemaDirectiveVisitor]] = {} + for type_def in type_defs: + visitor = getattr(type_def, "__visitor__", None) + if visitor and issubclass(visitor, SchemaDirectiveVisitor): + directives[type_def.graphql_name] = visitor + + if directives: + SchemaDirectiveVisitor.visit_schema_directives(schema, directives) + + +def repair_default_enum_values(schema, types_list: List[Type[DefinitionType]]) -> None: + for type_ in types_list: + if issubclass(type_, EnumType): + type_.__bind_to_default_values__(schema) diff --git a/ariadne_graphql_modules/input_type.py b/ariadne_graphql_modules/v1/input_type.py similarity index 97% rename from ariadne_graphql_modules/input_type.py rename to ariadne_graphql_modules/v1/input_type.py index 32295db..fa2eedb 100644 --- a/ariadne_graphql_modules/input_type.py +++ b/ariadne_graphql_modules/v1/input_type.py @@ -8,8 +8,8 @@ from .bases import BindableType from .dependencies import Dependencies, get_dependencies_from_input_type -from .types import InputFieldsDict, RequirementsDict -from .utils import parse_definition +from ..types import InputFieldsDict, RequirementsDict +from ..utils import parse_definition Args = Dict[str, str] InputNodeType = Union[InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode] diff --git a/ariadne_graphql_modules/interface_type.py b/ariadne_graphql_modules/v1/interface_type.py similarity index 98% rename from ariadne_graphql_modules/interface_type.py rename to ariadne_graphql_modules/v1/interface_type.py index ac34188..c51f28f 100644 --- a/ariadne_graphql_modules/interface_type.py +++ b/ariadne_graphql_modules/v1/interface_type.py @@ -16,8 +16,8 @@ from .bases import BindableType from .dependencies import Dependencies, get_dependencies_from_object_type from .resolvers_mixin import ResolversMixin -from .types import FieldsDict, RequirementsDict -from .utils import parse_definition +from ..types import FieldsDict, RequirementsDict +from ..utils import parse_definition InterfaceNodeType = Union[InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode] diff --git a/ariadne_graphql_modules/mutation_type.py b/ariadne_graphql_modules/v1/mutation_type.py similarity index 98% rename from ariadne_graphql_modules/mutation_type.py rename to ariadne_graphql_modules/v1/mutation_type.py index d8e020d..f579d3f 100644 --- a/ariadne_graphql_modules/mutation_type.py +++ b/ariadne_graphql_modules/v1/mutation_type.py @@ -10,8 +10,8 @@ from .bases import BindableType from .dependencies import Dependencies, get_dependencies_from_object_type -from .types import RequirementsDict -from .utils import parse_definition +from ..types import RequirementsDict +from ..utils import parse_definition MutationArgs = Dict[str, str] ObjectNodeType = Union[ObjectTypeDefinitionNode, ObjectTypeExtensionNode] diff --git a/ariadne_graphql_modules/object_type.py b/ariadne_graphql_modules/v1/object_type.py similarity index 98% rename from ariadne_graphql_modules/object_type.py rename to ariadne_graphql_modules/v1/object_type.py index bb54b4a..e5183a6 100644 --- a/ariadne_graphql_modules/object_type.py +++ b/ariadne_graphql_modules/v1/object_type.py @@ -10,8 +10,8 @@ from .bases import BindableType from .dependencies import Dependencies, get_dependencies_from_object_type from .resolvers_mixin import ResolversMixin -from .types import FieldsDict, RequirementsDict -from .utils import parse_definition +from ..types import FieldsDict, RequirementsDict +from ..utils import parse_definition ObjectNodeType = Union[ObjectTypeDefinitionNode, ObjectTypeExtensionNode] diff --git a/ariadne_graphql_modules/resolvers_mixin.py b/ariadne_graphql_modules/v1/resolvers_mixin.py similarity index 97% rename from ariadne_graphql_modules/resolvers_mixin.py rename to ariadne_graphql_modules/v1/resolvers_mixin.py index 9d641dc..ec24112 100644 --- a/ariadne_graphql_modules/resolvers_mixin.py +++ b/ariadne_graphql_modules/v1/resolvers_mixin.py @@ -2,8 +2,8 @@ from graphql import GraphQLFieldResolver, NamedTypeNode -from .types import FieldsDict -from .utils import create_alias_resolver +from ..types import FieldsDict +from ..utils import create_alias_resolver Aliases = Dict[str, str] FieldsArgs = Dict[str, Dict[str, str]] diff --git a/ariadne_graphql_modules/scalar_type.py b/ariadne_graphql_modules/v1/scalar_type.py similarity index 97% rename from ariadne_graphql_modules/scalar_type.py rename to ariadne_graphql_modules/v1/scalar_type.py index a86388b..3da456d 100644 --- a/ariadne_graphql_modules/scalar_type.py +++ b/ariadne_graphql_modules/v1/scalar_type.py @@ -12,8 +12,8 @@ ) from .bases import BindableType -from .types import RequirementsDict -from .utils import parse_definition +from ..types import RequirementsDict +from ..utils import parse_definition ScalarNodeType = Union[ScalarTypeDefinitionNode, ScalarTypeExtensionNode] diff --git a/ariadne_graphql_modules/subscription_type.py b/ariadne_graphql_modules/v1/subscription_type.py similarity index 100% rename from ariadne_graphql_modules/subscription_type.py rename to ariadne_graphql_modules/v1/subscription_type.py diff --git a/ariadne_graphql_modules/union_type.py b/ariadne_graphql_modules/v1/union_type.py similarity index 97% rename from ariadne_graphql_modules/union_type.py rename to ariadne_graphql_modules/v1/union_type.py index 932ba4f..94b8575 100644 --- a/ariadne_graphql_modules/union_type.py +++ b/ariadne_graphql_modules/v1/union_type.py @@ -11,8 +11,8 @@ from .bases import BindableType from .dependencies import Dependencies, get_dependencies_from_union_type -from .types import RequirementsDict -from .utils import parse_definition +from ..types import RequirementsDict +from ..utils import parse_definition UnionNodeType = Union[UnionTypeDefinitionNode, UnionTypeExtensionNode] diff --git a/ariadne_graphql_modules/next/validators.py b/ariadne_graphql_modules/validators.py similarity index 100% rename from ariadne_graphql_modules/next/validators.py rename to ariadne_graphql_modules/validators.py diff --git a/ariadne_graphql_modules/next/value.py b/ariadne_graphql_modules/value.py similarity index 100% rename from ariadne_graphql_modules/next/value.py rename to ariadne_graphql_modules/value.py diff --git a/pyproject.toml b/pyproject.toml index bb2236b..efdba0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ include = [ "ariadne_graphql_modules/**/*.py", "ariadne_graphql_modules/py.typed", ] -exclude = ["tests"] +exclude = ["tests", "tests_v1"] [tool.hatch.envs.default] features = ["test"] @@ -74,5 +74,5 @@ exclude = ''' ''' [tool.pytest.ini_options] -testpaths = ["tests_next"] +testpaths = ["tests"] asyncio_mode = "strict" diff --git a/tests/conftest.py b/tests/conftest.py index 0f16709..c66282b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,33 @@ from pathlib import Path +from textwrap import dedent + import pytest +from graphql import TypeDefinitionNode, GraphQLSchema, print_ast, print_schema + +from ariadne_graphql_modules import GraphQLMetadata + + +@pytest.fixture +def assert_schema_equals(): + def schema_equals_assertion(schema: GraphQLSchema, target: str): + schema_str = print_schema(schema) + assert schema_str == dedent(target).strip() + + return schema_equals_assertion + + +@pytest.fixture +def assert_ast_equals(): + def ast_equals_assertion(ast: TypeDefinitionNode, target: str): + ast_str = print_ast(ast) + assert ast_str == dedent(target).strip() + + return ast_equals_assertion + + +@pytest.fixture +def metadata(): + return GraphQLMetadata() @pytest.fixture(scope="session") diff --git a/tests_next/snapshots/test_arg_with_description_in_source_with_schema.obtained.yml b/tests/snapshots/test_arg_with_description_in_source_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_description_in_source_with_schema.obtained.yml rename to tests/snapshots/test_arg_with_description_in_source_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_arg_with_description_in_source_with_schema.yml b/tests/snapshots/test_arg_with_description_in_source_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_description_in_source_with_schema.yml rename to tests/snapshots/test_arg_with_description_in_source_with_schema.yml diff --git a/tests_next/snapshots/test_arg_with_name_in_source_with_schema.obtained.yml b/tests/snapshots/test_arg_with_name_in_source_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_name_in_source_with_schema.obtained.yml rename to tests/snapshots/test_arg_with_name_in_source_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_arg_with_name_in_source_with_schema.yml b/tests/snapshots/test_arg_with_name_in_source_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_name_in_source_with_schema.yml rename to tests/snapshots/test_arg_with_name_in_source_with_schema.yml diff --git a/tests_next/snapshots/test_arg_with_type_in_source_with_schema.obtained.yml b/tests/snapshots/test_arg_with_type_in_source_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_type_in_source_with_schema.obtained.yml rename to tests/snapshots/test_arg_with_type_in_source_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_arg_with_type_in_source_with_schema.yml b/tests/snapshots/test_arg_with_type_in_source_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_arg_with_type_in_source_with_schema.yml rename to tests/snapshots/test_arg_with_type_in_source_with_schema.yml diff --git a/tests_next/snapshots/test_deferred_raises_error_for_invalid_relative_path.obtained.yml b/tests/snapshots/test_deferred_raises_error_for_invalid_relative_path.obtained.yml similarity index 100% rename from tests_next/snapshots/test_deferred_raises_error_for_invalid_relative_path.obtained.yml rename to tests/snapshots/test_deferred_raises_error_for_invalid_relative_path.obtained.yml diff --git a/tests_next/snapshots/test_deferred_raises_error_for_invalid_relative_path.yml b/tests/snapshots/test_deferred_raises_error_for_invalid_relative_path.yml similarity index 100% rename from tests_next/snapshots/test_deferred_raises_error_for_invalid_relative_path.yml rename to tests/snapshots/test_deferred_raises_error_for_invalid_relative_path.yml diff --git a/tests_next/snapshots/test_description_not_str_without_schema.obtained.yml b/tests/snapshots/test_description_not_str_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_description_not_str_without_schema.obtained.yml rename to tests/snapshots/test_description_not_str_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_description_not_str_without_schema.yml b/tests/snapshots/test_description_not_str_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_description_not_str_without_schema.yml rename to tests/snapshots/test_description_not_str_without_schema.yml diff --git a/tests_next/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.obtained.yml b/tests/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.obtained.yml rename to tests/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.yml b/tests/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.yml rename to tests/snapshots/test_description_validator_raises_error_for_type_with_two_descriptions.yml diff --git a/tests_next/snapshots/test_enum_type_validation_fails_for_invalid_members.obtained.yml b/tests/snapshots/test_enum_type_validation_fails_for_invalid_members.obtained.yml similarity index 100% rename from tests_next/snapshots/test_enum_type_validation_fails_for_invalid_members.obtained.yml rename to tests/snapshots/test_enum_type_validation_fails_for_invalid_members.obtained.yml diff --git a/tests_next/snapshots/test_enum_type_validation_fails_for_invalid_members.yml b/tests/snapshots/test_enum_type_validation_fails_for_invalid_members.yml similarity index 100% rename from tests_next/snapshots/test_enum_type_validation_fails_for_invalid_members.yml rename to tests/snapshots/test_enum_type_validation_fails_for_invalid_members.yml diff --git a/tests_next/snapshots/test_enum_type_validation_fails_for_missing_members.obtained.yml b/tests/snapshots/test_enum_type_validation_fails_for_missing_members.obtained.yml similarity index 100% rename from tests_next/snapshots/test_enum_type_validation_fails_for_missing_members.obtained.yml rename to tests/snapshots/test_enum_type_validation_fails_for_missing_members.obtained.yml diff --git a/tests_next/snapshots/test_enum_type_validation_fails_for_missing_members.yml b/tests/snapshots/test_enum_type_validation_fails_for_missing_members.yml similarity index 100% rename from tests_next/snapshots/test_enum_type_validation_fails_for_missing_members.yml rename to tests/snapshots/test_enum_type_validation_fails_for_missing_members.yml diff --git a/tests_next/snapshots/test_field_name_not_str_without_schema.obtained.yml b/tests/snapshots/test_field_name_not_str_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_field_name_not_str_without_schema.obtained.yml rename to tests/snapshots/test_field_name_not_str_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_field_name_not_str_without_schema.yml b/tests/snapshots/test_field_name_not_str_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_field_name_not_str_without_schema.yml rename to tests/snapshots/test_field_name_not_str_without_schema.yml diff --git a/tests_next/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.obtained.yml b/tests/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.obtained.yml similarity index 100% rename from tests_next/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.obtained.yml rename to tests/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.obtained.yml diff --git a/tests_next/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.yml b/tests/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.yml similarity index 100% rename from tests_next/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.yml rename to tests/snapshots/test_input_type_instance_with_invalid_attrs_raising_error.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_out_names_without_schema.obtained.yml b/tests/snapshots/test_input_type_validation_fails_for_out_names_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_out_names_without_schema.obtained.yml rename to tests/snapshots/test_input_type_validation_fails_for_out_names_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_out_names_without_schema.yml b/tests/snapshots/test_input_type_validation_fails_for_out_names_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_out_names_without_schema.yml rename to tests/snapshots/test_input_type_validation_fails_for_out_names_without_schema.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.obtained.yml b/tests/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.obtained.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.obtained.yml rename to tests/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.obtained.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.yml b/tests/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.yml rename to tests/snapshots/test_input_type_validation_fails_for_unsupported_attr_default.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.obtained.yml b/tests/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.obtained.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.obtained.yml rename to tests/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.obtained.yml diff --git a/tests_next/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.yml b/tests/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.yml similarity index 100% rename from tests_next/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.yml rename to tests/snapshots/test_input_type_validation_fails_for_unsupported_field_default_option.yml diff --git a/tests/snapshots/test_interface_no_interface_in_schema.obtained.diff.html b/tests/snapshots/test_interface_no_interface_in_schema.obtained.diff.html new file mode 100644 index 0000000..c6a19d9 --- /dev/null +++ b/tests/snapshots/test_interface_no_interface_in_schema.obtained.diff.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + +

/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests_next/snapshots/test_interface_no_interface_in_schema.yml
/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests_next/snapshots/test_interface_no_interface_in_schema.obtained.yml
t1Unknown type 'BaseInterface'.t1Query root type must be provided.
2...2...
+ + + + +
Legends
+ + + + +
Colors
 Added 
Changed
Deleted
+ + + + +
Links
(f)irst change
(n)ext change
(t)op
+ + + \ No newline at end of file diff --git a/tests_next/snapshots/test_interface_no_interface_in_schema.obtained.yml b/tests/snapshots/test_interface_no_interface_in_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_interface_no_interface_in_schema.obtained.yml rename to tests/snapshots/test_interface_no_interface_in_schema.obtained.yml diff --git a/tests_next/snapshots/test_interface_no_interface_in_schema.yml b/tests/snapshots/test_interface_no_interface_in_schema.yml similarity index 100% rename from tests_next/snapshots/test_interface_no_interface_in_schema.yml rename to tests/snapshots/test_interface_no_interface_in_schema.yml diff --git a/tests/snapshots/test_interface_with_different_types.obtained.diff.html b/tests/snapshots/test_interface_with_different_types.obtained.diff.html new file mode 100644 index 0000000..ef57772 --- /dev/null +++ b/tests/snapshots/test_interface_with_different_types.obtained.diff.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + +

/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests_next/snapshots/test_interface_with_different_types.yml
/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests_next/snapshots/test_interface_with_different_types.obtained.yml
t1'Query root type must be provided.t1Query root type must be provided.
2 2...
3 
4  Interface field UserInterface.score expects type String! but User.score is type
5  Int!.'
+ + + + +
Legends
+ + + + +
Colors
 Added 
Changed
Deleted
+ + + + +
Links
(f)irst change
(n)ext change
(t)op
+ + + \ No newline at end of file diff --git a/tests/snapshots/test_interface_with_different_types.obtained.yml b/tests/snapshots/test_interface_with_different_types.obtained.yml new file mode 100644 index 0000000..322fe60 --- /dev/null +++ b/tests/snapshots/test_interface_with_different_types.obtained.yml @@ -0,0 +1,2 @@ +Query root type must be provided. +... diff --git a/tests_next/snapshots/test_interface_with_different_types.yml b/tests/snapshots/test_interface_with_different_types.yml similarity index 100% rename from tests_next/snapshots/test_interface_with_different_types.yml rename to tests/snapshots/test_interface_with_different_types.yml diff --git a/tests_next/snapshots/test_invalid_arg_name_in_source_with_schema.obtained.yml b/tests/snapshots/test_invalid_arg_name_in_source_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_invalid_arg_name_in_source_with_schema.obtained.yml rename to tests/snapshots/test_invalid_arg_name_in_source_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_invalid_arg_name_in_source_with_schema.yml b/tests/snapshots/test_invalid_arg_name_in_source_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_invalid_arg_name_in_source_with_schema.yml rename to tests/snapshots/test_invalid_arg_name_in_source_with_schema.yml diff --git a/tests_next/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.obtained.yml b/tests/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.obtained.yml similarity index 100% rename from tests_next/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.obtained.yml rename to tests/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.obtained.yml diff --git a/tests_next/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.yml b/tests/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.yml similarity index 100% rename from tests_next/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.yml rename to tests/snapshots/test_make_executable_schema_raises_error_if_called_without_any_types.yml diff --git a/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.diff.html b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.diff.html new file mode 100644 index 0000000..4929bd2 --- /dev/null +++ b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.diff.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + +

/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests/snapshots/test_metadata_raises_key_error_for_unset_data.yml
/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml
t1'"No data is set for ''<class ''tests_next.test_metadata.QueryType''>''."'t1'"No data is set for ''<class ''tests.test_metadata.QueryType''>''."'
+ + + + +
Legends
+ + + + +
Colors
 Added 
Changed
Deleted
+ + + + +
Links
(f)irst change
(n)ext change
(t)op
+ + + \ No newline at end of file diff --git a/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml new file mode 100644 index 0000000..ef7ccb8 --- /dev/null +++ b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml @@ -0,0 +1 @@ +'"No data is set for ''''."' diff --git a/tests/snapshots/test_metadata_raises_key_error_for_unset_data.yml b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.yml new file mode 100644 index 0000000..ef7ccb8 --- /dev/null +++ b/tests/snapshots/test_metadata_raises_key_error_for_unset_data.yml @@ -0,0 +1 @@ +'"No data is set for ''''."' diff --git a/tests_next/snapshots/test_missing_type_in_schema.obtained.yml b/tests/snapshots/test_missing_type_in_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_missing_type_in_schema.obtained.yml rename to tests/snapshots/test_missing_type_in_schema.obtained.yml diff --git a/tests_next/snapshots/test_missing_type_in_schema.yml b/tests/snapshots/test_missing_type_in_schema.yml similarity index 100% rename from tests_next/snapshots/test_missing_type_in_schema.yml rename to tests/snapshots/test_missing_type_in_schema.yml diff --git a/tests_next/snapshots/test_missing_type_in_types.obtained.yml b/tests/snapshots/test_missing_type_in_types.obtained.yml similarity index 100% rename from tests_next/snapshots/test_missing_type_in_types.obtained.yml rename to tests/snapshots/test_missing_type_in_types.obtained.yml diff --git a/tests_next/snapshots/test_missing_type_in_types.yml b/tests/snapshots/test_missing_type_in_types.yml similarity index 100% rename from tests_next/snapshots/test_missing_type_in_types.yml rename to tests/snapshots/test_missing_type_in_types.yml diff --git a/tests_next/snapshots/test_multiple_descriptions_for_source_with_schema.obtained.yml b/tests/snapshots/test_multiple_descriptions_for_source_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_multiple_descriptions_for_source_with_schema.obtained.yml rename to tests/snapshots/test_multiple_descriptions_for_source_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_multiple_descriptions_for_source_with_schema.yml b/tests/snapshots/test_multiple_descriptions_for_source_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_multiple_descriptions_for_source_with_schema.yml rename to tests/snapshots/test_multiple_descriptions_for_source_with_schema.yml diff --git a/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.diff.html b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.diff.html new file mode 100644 index 0000000..1ffb12e --- /dev/null +++ b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.diff.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + +

/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml
/Users/d0cza/Projects/Ariadne/ariadne-graphql-modules/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml
t1Types 'SecondRoot' and '<class 'tests_next.test_make_executable_schema.test_multiple_roots_fail_validation_if_merge_roots_is_disabled.<locals>.FirstRoot'>'t1Types 'SecondRoot' and '<class 'tests.test_make_executable_schema.test_multiple_roots_fail_validation_if_merge_roots_is_disabled.<locals>.FirstRoot'>'
2  both define GraphQL type with name 'Query'.2  both define GraphQL type with name 'Query'.
3...3...
+ + + + +
Legends
+ + + + +
Colors
 Added 
Changed
Deleted
+ + + + +
Links
(f)irst change
(n)ext change
(t)op
+ + + \ No newline at end of file diff --git a/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml new file mode 100644 index 0000000..c22028c --- /dev/null +++ b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml @@ -0,0 +1,3 @@ +Types 'SecondRoot' and '.FirstRoot'>' + both define GraphQL type with name 'Query'. +... diff --git a/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml new file mode 100644 index 0000000..c22028c --- /dev/null +++ b/tests/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml @@ -0,0 +1,3 @@ +Types 'SecondRoot' and '.FirstRoot'>' + both define GraphQL type with name 'Query'. +... diff --git a/tests_next/snapshots/test_multiple_sourced_for_field_with_schema.obtained.yml b/tests/snapshots/test_multiple_sourced_for_field_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_multiple_sourced_for_field_with_schema.obtained.yml rename to tests/snapshots/test_multiple_sourced_for_field_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_multiple_sourced_for_field_with_schema.yml b/tests/snapshots/test_multiple_sourced_for_field_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_multiple_sourced_for_field_with_schema.yml rename to tests/snapshots/test_multiple_sourced_for_field_with_schema.yml diff --git a/tests_next/snapshots/test_multiple_sources_without_schema.obtained.yml b/tests/snapshots/test_multiple_sources_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_multiple_sources_without_schema.obtained.yml rename to tests/snapshots/test_multiple_sources_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_multiple_sources_without_schema.yml b/tests/snapshots/test_multiple_sources_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_multiple_sources_without_schema.yml rename to tests/snapshots/test_multiple_sources_without_schema.yml diff --git a/tests_next/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.obtained.yml b/tests/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.obtained.yml similarity index 100% rename from tests_next/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.obtained.yml rename to tests/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.obtained.yml diff --git a/tests_next/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.yml b/tests/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.yml similarity index 100% rename from tests_next/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.yml rename to tests/snapshots/test_name_validator_raises_error_for_name_and_definition_mismatch.yml diff --git a/tests_next/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.obtained.yml b/tests/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.obtained.yml rename to tests/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.obtained.yml diff --git a/tests_next/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.yml b/tests/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.yml similarity index 100% rename from tests_next/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.yml rename to tests/snapshots/test_object_type_instance_with_invalid_attrs_raising_error.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_alias_resolver.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_alias_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_alias_resolver.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_alias_resolver.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_alias_resolver.yml b/tests/snapshots/test_object_type_validation_fails_for_alias_resolver.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_alias_resolver.yml rename to tests/snapshots/test_object_type_validation_fails_for_alias_resolver.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_alias_target_resolver.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_alias_target_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_alias_target_resolver.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_alias_target_resolver.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_alias_target_resolver.yml b/tests/snapshots/test_object_type_validation_fails_for_alias_target_resolver.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_alias_target_resolver.yml rename to tests/snapshots/test_object_type_validation_fails_for_alias_target_resolver.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.yml b/tests/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.yml rename to tests/snapshots/test_object_type_validation_fails_for_attr_and_field_with_same_graphql_name.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.yml b/tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.yml rename to tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_args.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.yml b/tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.yml rename to tests/snapshots/test_object_type_validation_fails_for_field_with_multiple_descriptions.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_invalid_alias.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_invalid_alias.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_invalid_alias.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_invalid_alias.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_invalid_alias.yml b/tests/snapshots/test_object_type_validation_fails_for_invalid_alias.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_invalid_alias.yml rename to tests/snapshots/test_object_type_validation_fails_for_invalid_alias.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.yml b/tests/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.yml rename to tests/snapshots/test_object_type_validation_fails_for_missing_field_resolver_arg.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.yml b/tests/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.yml rename to tests/snapshots/test_object_type_validation_fails_for_missing_resolver_arg.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_attrs_with_same_graphql_name.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_field_resolvers.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.yml b/tests/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.yml rename to tests/snapshots/test_object_type_validation_fails_for_multiple_fields_with_same_graphql_name.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_attr_resolver.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_field_resolver_arg.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.yml b/tests/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.yml rename to tests/snapshots/test_object_type_validation_fails_for_undefined_resolver_arg.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.yml b/tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.yml rename to tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.obtained.yml b/tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.obtained.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.obtained.yml rename to tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.obtained.yml diff --git a/tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.yml b/tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.yml similarity index 100% rename from tests_next/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.yml rename to tests/snapshots/test_object_type_validation_fails_for_unsupported_resolver_arg_default_option.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_duplicated_members_descriptions.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_empty_enum.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_members_descriptions.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_invalid_type_schema.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_names_not_matching.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_dict_mismatch.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_enum_mismatch.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_schema_and_members_list.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.obtained.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.obtained.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.yml b/tests/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.yml rename to tests/snapshots/test_schema_enum_type_validation_fails_for_two_descriptions.yml diff --git a/tests_next/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.obtained.yml b/tests/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.obtained.yml rename to tests/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.yml b/tests/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.yml rename to tests/snapshots/test_schema_input_type_instance_with_invalid_attrs_raising_error.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_duplicate_out_name.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_invalid_out_name.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_invalid_type_schema.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_names_not_matching.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_schema_missing_fields.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.obtained.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.obtained.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.yml b/tests/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.yml rename to tests/snapshots/test_schema_input_type_validation_fails_for_two_descriptions.yml diff --git a/tests_next/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.obtained.yml b/tests/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.obtained.yml rename to tests/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.yml b/tests/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.yml rename to tests/snapshots/test_schema_object_type_instance_with_invalid_attrs_raising_error.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_alias_resolver.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_alias_target_resolver.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_double_description.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_name_option.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_arg_with_type_option.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_instance.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_instance.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_instance.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_instance.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_instance.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_instance.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_instance.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_instance.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_with_invalid_arg_name.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_field_with_multiple_descriptions.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_invalid_alias.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_invalid_type_schema.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_missing_fields.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_missing_fields.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_missing_fields.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_missing_fields.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_missing_fields.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_missing_fields.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_missing_fields.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_missing_fields.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_multiple_field_resolvers.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_names_not_matching.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_two_descriptions.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_undefined_field_resolver.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_default.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.obtained.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.obtained.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.obtained.yml diff --git a/tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.yml b/tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.yml similarity index 100% rename from tests_next/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.yml rename to tests/snapshots/test_schema_object_type_validation_fails_for_unsupported_resolver_arg_option_default.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_different_names.obtained.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_different_names.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_different_names.obtained.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_different_names.obtained.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_different_names.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_different_names.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_different_names.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_different_names.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.obtained.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.obtained.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.obtained.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_invalid_type_schema.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.obtained.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.obtained.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.obtained.yml diff --git a/tests_next/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.yml b/tests/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.yml similarity index 100% rename from tests_next/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.yml rename to tests/snapshots/test_schema_scalar_type_validation_fails_for_two_descriptions.yml diff --git a/tests_next/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.obtained.yml b/tests/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.obtained.yml similarity index 100% rename from tests_next/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.obtained.yml rename to tests/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.obtained.yml diff --git a/tests_next/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.yml b/tests/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.yml similarity index 100% rename from tests_next/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.yml rename to tests/snapshots/test_schema_validation_fails_if_lazy_type_doesnt_exist.yml diff --git a/tests_next/snapshots/test_source_args_field_arg_not_dict_without_schema.obtained.yml b/tests/snapshots/test_source_args_field_arg_not_dict_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_source_args_field_arg_not_dict_without_schema.obtained.yml rename to tests/snapshots/test_source_args_field_arg_not_dict_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_source_args_field_arg_not_dict_without_schema.yml b/tests/snapshots/test_source_args_field_arg_not_dict_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_source_args_field_arg_not_dict_without_schema.yml rename to tests/snapshots/test_source_args_field_arg_not_dict_without_schema.yml diff --git a/tests_next/snapshots/test_source_args_not_dict_without_schema.obtained.yml b/tests/snapshots/test_source_args_not_dict_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_source_args_not_dict_without_schema.obtained.yml rename to tests/snapshots/test_source_args_not_dict_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_source_args_not_dict_without_schema.yml b/tests/snapshots/test_source_args_not_dict_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_source_args_not_dict_without_schema.yml rename to tests/snapshots/test_source_args_not_dict_without_schema.yml diff --git a/tests_next/snapshots/test_source_for_undefined_field_with_schema.obtained.yml b/tests/snapshots/test_source_for_undefined_field_with_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_source_for_undefined_field_with_schema.obtained.yml rename to tests/snapshots/test_source_for_undefined_field_with_schema.obtained.yml diff --git a/tests_next/snapshots/test_source_for_undefined_field_with_schema.yml b/tests/snapshots/test_source_for_undefined_field_with_schema.yml similarity index 100% rename from tests_next/snapshots/test_source_for_undefined_field_with_schema.yml rename to tests/snapshots/test_source_for_undefined_field_with_schema.yml diff --git a/tests_next/snapshots/test_undefined_name_without_schema.obtained.yml b/tests/snapshots/test_undefined_name_without_schema.obtained.yml similarity index 100% rename from tests_next/snapshots/test_undefined_name_without_schema.obtained.yml rename to tests/snapshots/test_undefined_name_without_schema.obtained.yml diff --git a/tests_next/snapshots/test_undefined_name_without_schema.yml b/tests/snapshots/test_undefined_name_without_schema.yml similarity index 100% rename from tests_next/snapshots/test_undefined_name_without_schema.yml rename to tests/snapshots/test_undefined_name_without_schema.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.obtained.yml b/tests/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.obtained.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.obtained.yml rename to tests/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.obtained.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.yml b/tests/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.yml rename to tests/snapshots/test_value_error_is_raised_if_exclude_and_include_members_are_combined.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.obtained.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.obtained.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.obtained.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.obtained.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_excluded_item.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.obtained.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.obtained.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.obtained.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.obtained.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_missing_item.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.obtained.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.obtained.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.obtained.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.obtained.yml diff --git a/tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.yml b/tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.yml similarity index 100% rename from tests_next/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.yml rename to tests/snapshots/test_value_error_is_raised_if_member_description_is_set_for_omitted_item.yml diff --git a/tests_next/test_compatibility_layer.py b/tests/test_compatibility_layer.py similarity index 93% rename from tests_next/test_compatibility_layer.py rename to tests/test_compatibility_layer.py index 320bdc4..90dc845 100644 --- a/tests_next/test_compatibility_layer.py +++ b/tests/test_compatibility_layer.py @@ -2,17 +2,17 @@ from datetime import date, datetime from graphql import StringValueNode -from ariadne_graphql_modules.bases import DeferredType -from ariadne_graphql_modules.collection_type import CollectionType -from ariadne_graphql_modules.enum_type import EnumType -from ariadne_graphql_modules.input_type import InputType -from ariadne_graphql_modules.interface_type import InterfaceType -from ariadne_graphql_modules.next.compatibility_layer import wrap_legacy_types -from ariadne_graphql_modules.next.executable_schema import make_executable_schema -from ariadne_graphql_modules.object_type import ObjectType -from ariadne_graphql_modules.scalar_type import ScalarType -from ariadne_graphql_modules.subscription_type import SubscriptionType -from ariadne_graphql_modules.union_type import UnionType +from ariadne_graphql_modules.v1.bases import DeferredType +from ariadne_graphql_modules.v1.collection_type import CollectionType +from ariadne_graphql_modules.v1.enum_type import EnumType +from ariadne_graphql_modules.v1.input_type import InputType +from ariadne_graphql_modules.v1.interface_type import InterfaceType +from ariadne_graphql_modules.compatibility_layer import wrap_legacy_types +from ariadne_graphql_modules.executable_schema import make_executable_schema +from ariadne_graphql_modules.v1.object_type import ObjectType +from ariadne_graphql_modules.v1.scalar_type import ScalarType +from ariadne_graphql_modules.v1.subscription_type import SubscriptionType +from ariadne_graphql_modules.v1.union_type import UnionType def test_object_type( diff --git a/tests_next/test_deferred_type.py b/tests/test_deferred_type.py similarity index 73% rename from tests_next/test_deferred_type.py rename to tests/test_deferred_type.py index 964e1a3..7f72937 100644 --- a/tests_next/test_deferred_type.py +++ b/tests/test_deferred_type.py @@ -2,25 +2,25 @@ import pytest -from ariadne_graphql_modules.next import deferred +from ariadne_graphql_modules import deferred def test_deferred_returns_deferred_type_with_abs_path(): - deferred_type = deferred("tests_next.types") - assert deferred_type.path == "tests_next.types" + deferred_type = deferred("tests.types") + assert deferred_type.path == "tests.types" def test_deferred_returns_deferred_type_with_relative_path(): class MockType: deferred_type = deferred(".types") - assert MockType.deferred_type.path == "tests_next.types" + assert MockType.deferred_type.path == "tests.types" def test_deferred_returns_deferred_type_with_higher_level_relative_path(monkeypatch): frame_mock = Mock(f_globals={"__package__": "lorem.ipsum"}) monkeypatch.setattr( - "ariadne_graphql_modules.next.deferredtype.sys._getframe", + "ariadne_graphql_modules.deferredtype.sys._getframe", Mock(return_value=frame_mock), ) @@ -33,7 +33,7 @@ class MockType: def test_deferred_raises_error_for_invalid_relative_path(monkeypatch, data_regression): frame_mock = Mock(f_globals={"__package__": "lorem"}) monkeypatch.setattr( - "ariadne_graphql_modules.next.deferredtype.sys._getframe", + "ariadne_graphql_modules.deferredtype.sys._getframe", Mock(return_value=frame_mock), ) diff --git a/tests_next/test_description_node.py b/tests/test_description_node.py similarity index 94% rename from tests_next/test_description_node.py rename to tests/test_description_node.py index e9f7101..19a6960 100644 --- a/tests_next/test_description_node.py +++ b/tests/test_description_node.py @@ -1,4 +1,4 @@ -from ariadne_graphql_modules.next import get_description_node +from ariadne_graphql_modules import get_description_node def test_no_description_is_returned_for_none(): diff --git a/tests/test_enum_type.py b/tests/test_enum_type.py index 7ea4585..8e051d7 100644 --- a/tests/test_enum_type.py +++ b/tests/test_enum_type.py @@ -1,304 +1,522 @@ from enum import Enum -import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, graphql_sync +from graphql import graphql_sync from ariadne_graphql_modules import ( - DirectiveType, - EnumType, - ObjectType, + GraphQLEnum, + GraphQLObject, make_executable_schema, ) -def test_enum_type_raises_attribute_error_when_defined_without_schema(data_regression): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - pass +class UserLevelEnum(Enum): + GUEST = 0 + MEMBER = 1 + ADMIN = 2 - data_regression.check(str(err.value)) +def test_enum_field_returning_enum_value(assert_schema_equals): + class UserLevel(GraphQLEnum): + __members__ = UserLevelEnum -def test_enum_type_raises_error_when_defined_with_invalid_schema_type(data_regression): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = True + class QueryType(GraphQLObject): + level: UserLevel - data_regression.check(str(err.value)) + @GraphQLObject.resolver("level") + def resolve_level(*_) -> UserLevelEnum: + return UserLevelEnum.MEMBER + schema = make_executable_schema(QueryType) -def test_enum_type_raises_error_when_defined_with_invalid_schema_str(data_regression): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = "enom UserRole" + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } - data_regression.check(str(err.value)) + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) + result = graphql_sync(schema, "{ level }") -def test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = "scalar UserRole" + assert not result.errors + assert result.data == {"level": "MEMBER"} - data_regression.check(str(err.value)) +def test_enum_field_returning_dict_value(assert_schema_equals): + class UserLevel(GraphQLEnum): + __members__ = { + "GUEST": 0, + "MEMBER": 1, + "ADMIN": 2, + } -def test_enum_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = """ - enum UserRole { - USER - MOD - ADMIN - } + class QueryType(GraphQLObject): + level: UserLevel + + @GraphQLObject.resolver("level") + def resolve_level(*_) -> dict: + return 0 + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) + + result = graphql_sync(schema, "{ level }") + + assert not result.errors + assert result.data == {"level": "GUEST"} + + +def test_enum_field_returning_str_value(assert_schema_equals): + class UserLevel(GraphQLEnum): + __members__ = [ + "GUEST", + "MEMBER", + "ADMIN", + ] + + class QueryType(GraphQLObject): + level: UserLevel + + @GraphQLObject.resolver("level") + def resolve_level(*_) -> str: + return "ADMIN" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) + + result = graphql_sync(schema, "{ level }") + + assert not result.errors + assert result.data == {"level": "ADMIN"} + + +def test_enum_type_with_custom_name(assert_schema_equals): + class UserLevel(GraphQLEnum): + __graphql_name__ = "UserLevelEnum" + __members__ = UserLevelEnum + + class QueryType(GraphQLObject): + level: UserLevel + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevelEnum! + } + + enum UserLevelEnum { + GUEST + MEMBER + ADMIN + } + """, + ) + + +def test_enum_type_with_description(assert_schema_equals): + class UserLevel(GraphQLEnum): + __description__ = "Hello world." + __members__ = UserLevelEnum + + class QueryType(GraphQLObject): + level: UserLevel + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + \"\"\"Hello world.\"\"\" + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) + + +def test_enum_type_with_member_description(assert_schema_equals): + class UserLevel(GraphQLEnum): + __members__ = UserLevelEnum + __members_descriptions__ = {"MEMBER": "Hello world."} + + class QueryType(GraphQLObject): + level: UserLevel - enum Category { - CATEGORY - LINK + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + + \"\"\"Hello world.\"\"\" + MEMBER + ADMIN + } + """, + ) + + +def test_schema_enum_field_returning_enum_value(assert_schema_equals): + class UserLevel(GraphQLEnum): + __schema__ = """ + enum UserLevel { + GUEST + MEMBER + ADMIN } """ + __members__ = UserLevelEnum + + class QueryType(GraphQLObject): + level: UserLevel + + @GraphQLObject.resolver("level") + def resolve_level(*_) -> UserLevelEnum: + return UserLevelEnum.MEMBER + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) + + result = graphql_sync(schema, "{ level }") - data_regression.check(str(err.value)) + assert not result.errors + assert result.data == {"level": "MEMBER"} -def test_enum_type_extracts_graphql_name(): - class UserRoleEnum(EnumType): +def test_schema_enum_field_returning_dict_value(assert_schema_equals): + class UserLevel(GraphQLEnum): __schema__ = """ - enum UserRole { - USER - MOD - ADMIN + enum UserLevel { + GUEST + MEMBER + ADMIN } + """ + __members__ = { + "GUEST": 0, + "MEMBER": 1, + "ADMIN": 2, + } + + class QueryType(GraphQLObject): + level: UserLevel + + @GraphQLObject.resolver("level") + def resolve_level(*_) -> int: + return 2 + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) - assert UserRoleEnum.graphql_name == "UserRole" + result = graphql_sync(schema, "{ level }") + assert not result.errors + assert result.data == {"level": "ADMIN"} -def test_enum_type_can_be_extended_with_new_values(): - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): + +def test_schema_enum_field_returning_str_value(assert_schema_equals): + class UserLevel(GraphQLEnum): __schema__ = """ - enum UserRole { - USER - MOD - ADMIN + enum UserLevel { + GUEST + MEMBER + ADMIN } + """ + + class QueryType(GraphQLObject): + level: UserLevel + + @GraphQLObject.resolver("level") + def resolve_level(*_) -> str: + return "GUEST" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + level: UserLevel! + } - class ExtendUserRoleEnum(EnumType): - __schema__ = """ - extend enum UserRole { - MVP + enum UserLevel { + GUEST + MEMBER + ADMIN } - """ - __requires__ = [UserRoleEnum] + """, + ) + + result = graphql_sync(schema, "{ level }") + assert not result.errors + assert result.data == {"level": "GUEST"} -def test_enum_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on ENUM" - __visitor__ = SchemaDirectiveVisitor - class UserRoleEnum(EnumType): +def test_schema_enum_with_description_attr(assert_schema_equals): + class UserLevel(GraphQLEnum): __schema__ = """ - enum UserRole { - USER - MOD - ADMIN + enum UserLevel { + GUEST + MEMBER + ADMIN } - """ + """ + __members__ = { + "GUEST": 0, + "MEMBER": 1, + "ADMIN": 2, + } + __description__ = "Hello world." - class ExtendUserRoleEnum(EnumType): - __schema__ = "extend enum UserRole @example" - __requires__ = [UserRoleEnum, ExampleDirective] + class QueryType(GraphQLObject): + level: UserLevel + @GraphQLObject.resolver("level") + def resolve_level(*_) -> int: + return 2 -class BaseQueryType(ObjectType): - __abstract__ = True - __schema__ = """ - type Query { - enumToRepr(enum: UserRole = USER): String! - reprToEnum: UserRole! - } - """ - __aliases__ = { - "enumToRepr": "enum_repr", - } + schema = make_executable_schema(QueryType) - @staticmethod - def resolve_enum_repr(*_, enum) -> str: - return repr(enum) + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + \"\"\"Hello world.\"\"\" + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) -def make_test_schema(enum_type): - class QueryType(BaseQueryType): - __requires__ = [enum_type] + result = graphql_sync(schema, "{ level }") - return make_executable_schema(QueryType) + assert not result.errors + assert result.data == {"level": "ADMIN"} -def test_enum_type_can_be_defined_with_dict_mapping(): - class UserRoleEnum(EnumType): +def test_schema_enum_with_schema_description(assert_schema_equals): + class UserLevel(GraphQLEnum): __schema__ = """ - enum UserRole { - USER - MOD - ADMIN + \"\"\"Hello world.\"\"\" + enum UserLevel { + GUEST + MEMBER + ADMIN } - """ - __enum__ = { - "USER": 0, - "MOD": 1, + """ + __members__ = { + "GUEST": 0, + "MEMBER": 1, "ADMIN": 2, } - schema = make_test_schema(UserRoleEnum) + class QueryType(GraphQLObject): + level: UserLevel - # Specfied enum value is reversed - result = graphql_sync(schema, "{ enumToRepr(enum: MOD) }") - assert result.data["enumToRepr"] == "1" + @GraphQLObject.resolver("level") + def resolve_level(*_) -> int: + return 2 - # Default enum value is reversed - result = graphql_sync(schema, "{ enumToRepr }") - assert result.data["enumToRepr"] == "0" + schema = make_executable_schema(QueryType) - # Python value is converted to enum - result = graphql_sync(schema, "{ reprToEnum }", root_value={"reprToEnum": 2}) - assert result.data["reprToEnum"] == "ADMIN" + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + \"\"\"Hello world.\"\"\" + enum UserLevel { + GUEST + MEMBER + ADMIN + } + """, + ) -def test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = """ - enum UserRole { - USER - MOD - ADMIN - } - """ - __enum__ = { - "USER": 0, - "MODERATOR": 1, - "ADMIN": 2, - } + result = graphql_sync(schema, "{ level }") - data_regression.check(str(err.value)) - - -def test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = """ - enum UserRole { - USER - MOD - ADMIN - } - """ - __enum__ = { - "USER": 0, - "REVIEW": 1, - "MOD": 2, - "ADMIN": 3, + assert not result.errors + assert result.data == {"level": "ADMIN"} + + +def test_schema_enum_with_member_description(assert_schema_equals): + class UserLevel(GraphQLEnum): + __schema__ = """ + enum UserLevel { + GUEST + MEMBER + ADMIN } + """ + __members__ = { + "GUEST": 0, + "MEMBER": 1, + "ADMIN": 2, + } + __members_descriptions__ = {"MEMBER": "Hello world."} - data_regression.check(str(err.value)) + class QueryType(GraphQLObject): + level: UserLevel + @GraphQLObject.resolver("level") + def resolve_level(*_) -> int: + return 2 -def test_enum_type_can_be_defined_with_str_enum_mapping(): - class RoleEnum(str, Enum): - USER = "user" - MOD = "moderator" - ADMIN = "administrator" + schema = make_executable_schema(QueryType) - class UserRoleEnum(EnumType): + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + + \"\"\"Hello world.\"\"\" + MEMBER + ADMIN + } + """, + ) + + result = graphql_sync(schema, "{ level }") + + assert not result.errors + assert result.data == {"level": "ADMIN"} + + +def test_schema_enum_with_member_schema_description(assert_schema_equals): + class UserLevel(GraphQLEnum): __schema__ = """ - enum UserRole { - USER - MOD - ADMIN + enum UserLevel { + GUEST + \"\"\"Hello world.\"\"\" + MEMBER + ADMIN } - """ - __enum__ = RoleEnum + """ + __members__ = { + "GUEST": 0, + "MEMBER": 1, + "ADMIN": 2, + } - schema = make_test_schema(UserRoleEnum) + class QueryType(GraphQLObject): + level: UserLevel - # Specfied enum value is reversed - result = graphql_sync(schema, "{ enumToRepr(enum: MOD) }") - assert result.data["enumToRepr"] == repr(RoleEnum.MOD) + @GraphQLObject.resolver("level") + def resolve_level(*_) -> int: + return 2 - # Default enum value is reversed - result = graphql_sync(schema, "{ enumToRepr }") - assert result.data["enumToRepr"] == repr(RoleEnum.USER) + schema = make_executable_schema(QueryType) - # Python value is converted to enum - result = graphql_sync( - schema, "{ reprToEnum }", root_value={"reprToEnum": "administrator"} + assert_schema_equals( + schema, + """ + type Query { + level: UserLevel! + } + + enum UserLevel { + GUEST + + \"\"\"Hello world.\"\"\" + MEMBER + ADMIN + } + """, ) - assert result.data["reprToEnum"] == "ADMIN" - - -def test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition( - data_regression, -): - class RoleEnum(str, Enum): - USER = "user" - MODERATOR = "moderator" - ADMIN = "administrator" - - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = """ - enum UserRole { - USER - MOD - ADMIN - } - """ - __enum__ = RoleEnum - - data_regression.check(str(err.value)) - - -def test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition( - data_regression, -): - class RoleEnum(str, Enum): - USER = "user" - REVIEW = "review" - MOD = "moderator" - ADMIN = "administrator" - - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserRoleEnum(EnumType): - __schema__ = """ - enum UserRole { - USER - MOD - ADMIN - } - """ - __enum__ = RoleEnum - data_regression.check(str(err.value)) + result = graphql_sync(schema, "{ level }") + + assert not result.errors + assert result.data == {"level": "ADMIN"} diff --git a/tests_next/test_enum_type_validation.py b/tests/test_enum_type_validation.py similarity index 98% rename from tests_next/test_enum_type_validation.py rename to tests/test_enum_type_validation.py index 29e983a..a5106d1 100644 --- a/tests_next/test_enum_type_validation.py +++ b/tests/test_enum_type_validation.py @@ -2,8 +2,8 @@ import pytest -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import GraphQLEnum +from ariadne import gql +from ariadne_graphql_modules import GraphQLEnum def test_schema_enum_type_validation_fails_for_invalid_type_schema(data_regression): diff --git a/tests_next/test_get_field_args_from_resolver.py b/tests/test_get_field_args_from_resolver.py similarity index 98% rename from tests_next/test_get_field_args_from_resolver.py rename to tests/test_get_field_args_from_resolver.py index c7e0924..b281c28 100644 --- a/tests_next/test_get_field_args_from_resolver.py +++ b/tests/test_get_field_args_from_resolver.py @@ -1,4 +1,4 @@ -from ariadne_graphql_modules.next.graphql_object import get_field_args_from_resolver +from ariadne_graphql_modules.object_type import get_field_args_from_resolver def test_field_has_no_args_after_obj_and_info_args(): diff --git a/tests_next/test_id_type.py b/tests/test_id_type.py similarity index 98% rename from tests_next/test_id_type.py rename to tests/test_id_type.py index 1a4b2e1..9d53923 100644 --- a/tests_next/test_id_type.py +++ b/tests/test_id_type.py @@ -1,6 +1,6 @@ from graphql import graphql_sync -from ariadne_graphql_modules.next import ( +from ariadne_graphql_modules import ( GraphQLID, GraphQLInput, GraphQLObject, diff --git a/tests/test_input_type.py b/tests/test_input_type.py index daad5e0..d5d09bd 100644 --- a/tests/test_input_type.py +++ b/tests/test_input_type.py @@ -1,293 +1,628 @@ +from typing import Optional + import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, graphql_sync +from graphql import graphql_sync +from ariadne import gql from ariadne_graphql_modules import ( - DeferredType, - DirectiveType, - EnumType, - InputType, - InterfaceType, - ObjectType, - ScalarType, + GraphQLInput, + GraphQLObject, make_executable_schema, ) -def test_input_type_raises_attribute_error_when_defined_without_schema(data_regression): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - pass +def test_input_type_instance_with_all_attrs_values(): + class SearchInput(GraphQLInput): + query: str + age: int + + obj = SearchInput(query="search", age=20) + assert obj.query == "search" + assert obj.age == 20 + + +def test_input_type_instance_with_omitted_attrs_being_none(): + class SearchInput(GraphQLInput): + query: str + age: int + + obj = SearchInput(age=20) + assert obj.query is None + assert obj.age == 20 + + +def test_input_type_instance_with_default_attrs_values(): + class SearchInput(GraphQLInput): + query: str = "default" + age: int = 42 + + obj = SearchInput(age=20) + assert obj.query == "default" + assert obj.age == 20 + + +def test_input_type_instance_with_all_fields_values(): + class SearchInput(GraphQLInput): + query: str = GraphQLInput.field() + age: int = GraphQLInput.field() - data_regression.check(str(err.value)) + obj = SearchInput(query="search", age=20) + assert obj.query == "search" + assert obj.age == 20 -def test_input_type_raises_error_when_defined_with_invalid_schema_type(data_regression): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = True +def test_input_type_instance_with_all_fields_default_values(): + class SearchInput(GraphQLInput): + query: str = GraphQLInput.field(default_value="default") + age: int = GraphQLInput.field(default_value=42) - data_regression.check(str(err.value)) + obj = SearchInput(age=20) + assert obj.query == "default" + assert obj.age == 20 -def test_input_type_raises_error_when_defined_with_invalid_schema_str(data_regression): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = "inpet UserInput" +def test_input_type_instance_with_invalid_attrs_raising_error(data_regression): + class SearchInput(GraphQLInput): + query: str + age: int - data_regression.check(str(err.value)) + with pytest.raises(TypeError) as exc_info: + SearchInput(age=20, invalid="Ok") + data_regression.check(str(exc_info.value)) -def test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - type User { - id: ID! + +def test_schema_input_type_instance_with_all_attrs_values(): + class SearchInput(GraphQLInput): + __schema__ = gql( + """ + input Search { + query: String + age: Int } """ + ) - data_regression.check(str(err.value)) + query: str + age: int + obj = SearchInput(query="search", age=20) + assert obj.query == "search" + assert obj.age == 20 -def test_input_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - input User - input Group +def test_schema_input_type_instance_with_omitted_attrs_being_none(): + class SearchInput(GraphQLInput): + __schema__ = gql( """ + input Search { + query: String + age: Int + } + """ + ) - data_regression.check(str(err.value)) + query: str + age: int + obj = SearchInput(age=20) + assert obj.query is None + assert obj.age == 20 -def test_input_type_raises_error_when_defined_without_fields(data_regression): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = "input User" - data_regression.check(str(err.value)) +def test_schema_input_type_instance_with_default_attrs_values(): + class SearchInput(GraphQLInput): + __schema__ = gql( + """ + input Search { + query: String = "default" + age: Int = 42 + } + """ + ) + query: str + age: int -def test_input_type_extracts_graphql_name(): - class UserInput(InputType): - __schema__ = """ - input User { - id: ID! - } - """ + obj = SearchInput(age=20) + assert obj.query == "default" + assert obj.age == 20 - assert UserInput.graphql_name == "User" +def test_schema_input_type_instance_with_all_attrs_default_values(): + class SearchInput(GraphQLInput): + __schema__ = gql( + """ + input Search { + query: String = "default" + age: Int = 42 + } + """ + ) + + query: str + age: int + + obj = SearchInput() + assert obj.query == "default" + assert obj.age == 42 -def test_input_type_raises_error_when_defined_without_field_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - input User { - id: ID! - role: Role! + +def test_schema_input_type_instance_with_default_attrs_python_values(): + class SearchInput(GraphQLInput): + __schema__ = gql( + """ + input Search { + query: String + age: Int } """ + ) - data_regression.check(str(err.value)) + query: str = "default" + age: int = 42 + obj = SearchInput(age=20) + assert obj.query == "default" + assert obj.age == 20 -def test_input_type_verifies_field_dependency(): - # pylint: disable=unused-variable - class RoleEnum(EnumType): - __schema__ = """ - enum Role { - USER - ADMIN - } + +def test_schema_input_type_instance_with_invalid_attrs_raising_error(data_regression): + class SearchInput(GraphQLInput): + __schema__ = gql( + """ + input Search { + query: String + age: Int + } + """ + ) + + query: str + age: int + + with pytest.raises(TypeError) as exc_info: + SearchInput(age=20, invalid="Ok") + + data_regression.check(str(exc_info.value)) + + +def test_input_type_arg(assert_schema_equals): + class SearchInput(GraphQLInput): + query: Optional[str] + age: Optional[int] + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String + age: Int + } + """, + ) - class UserInput(InputType): + result = graphql_sync(schema, '{ search(input: { query: "Hello" }) }') + + assert not result.errors + assert result.data == {"search": "['Hello', None]"} + + +def test_schema_input_type_arg(assert_schema_equals): + class SearchInput(GraphQLInput): __schema__ = """ - input User { - id: ID! - role: Role! + input SearchInput { + query: String + age: Int } """ - __requires__ = [RoleEnum] + query: Optional[str] + age: Optional[int] -def test_input_type_verifies_circular_dependency(): - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - input User { - id: ID! - patron: User + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String + age: Int } + """, + ) + + result = graphql_sync(schema, '{ search(input: { query: "Hello" }) }') + + assert not result.errors + assert result.data == {"search": "['Hello', None]"} + + +def test_input_type_automatic_out_name_arg(assert_schema_equals): + class SearchInput(GraphQLInput): + query: Optional[str] + min_age: Optional[int] + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.min_age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String + minAge: Int + } + """, + ) + + result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") + + assert not result.errors + assert result.data == {"search": "[None, 21]"} -def test_input_type_verifies_circular_dependency_using_deferred_type(): - # pylint: disable=unused-variable - class GroupInput(InputType): +def test_schema_input_type_automatic_out_name_arg(assert_schema_equals): + class SearchInput(GraphQLInput): __schema__ = """ - input Group { - id: ID! - patron: User + input SearchInput { + query: String + minAge: Int } """ - __requires__ = [DeferredType("User")] - class UserInput(InputType): + query: Optional[str] + min_age: Optional[int] + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.min_age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String + minAge: Int + } + """, + ) + + result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") + + assert not result.errors + assert result.data == {"search": "[None, 21]"} + + +def test_schema_input_type_explicit_out_name_arg(assert_schema_equals): + class SearchInput(GraphQLInput): __schema__ = """ - input User { - id: ID! - group: Group + input SearchInput { + query: String + minAge: Int } """ - __requires__ = [GroupInput] + __out_names__ = {"minAge": "age"} + query: Optional[str] + age: Optional[int] -def test_input_type_can_be_extended_with_new_fields(): - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - input User { - id: ID! + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String + minAge: Int } + """, + ) + + result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") + + assert not result.errors + assert result.data == {"search": "[None, 21]"} + + +def test_input_type_self_reference(assert_schema_equals): + class SearchInput(GraphQLInput): + query: Optional[str] + extra: Optional["SearchInput"] + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + if input.extra: + extra_repr = input.extra.query + else: + extra_repr = None + + return f"{repr([input.query, extra_repr])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + search(input: SearchInput!): String! + } - class ExtendUserInput(InputType): - __schema__ = """ - extend input User { - name: String! + input SearchInput { + query: String + extra: SearchInput } + """, + ) + + result = graphql_sync( + schema, """ - __requires__ = [UserInput] + { + search( + input: { query: "Hello", extra: { query: "Other" } } + ) + } + """, + ) + assert not result.errors + assert result.data == {"search": "['Hello', 'Other']"} -def test_input_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on INPUT_OBJECT" - __visitor__ = SchemaDirectiveVisitor - class UserInput(InputType): +def test_schema_input_type_with_default_value(assert_schema_equals): + class SearchInput(GraphQLInput): __schema__ = """ - input User { - id: ID! + input SearchInput { + query: String = "Search" + age: Int = 42 } """ - class ExtendUserInput(InputType): - __schema__ = """ - extend input User @example + query: str + age: int + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [UserInput, ExampleDirective] - - -def test_input_type_raises_error_when_defined_without_extended_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExtendUserInput(InputType): - __schema__ = """ - extend input User { - name: String! - } - """ + type Query { + search(input: SearchInput!): String! + } - data_regression.check(str(err.value)) + input SearchInput { + query: String = "Search" + age: Int = 42 + } + """, + ) + result = graphql_sync(schema, "{ search(input: {}) }") -def test_input_type_raises_error_when_extended_dependency_is_wrong_type( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface User { - id: ID! - } - """ + assert not result.errors + assert result.data == {"search": "['Search', 42]"} - class ExtendUserInput(InputType): - __schema__ = """ - extend input User { - name: String! - } - """ - __requires__ = [ExampleInterface] - data_regression.check(str(err.value)) +def test_input_type_with_field_default_value(assert_schema_equals): + class SearchInput(GraphQLInput): + query: str = "default" + age: int = 42 + flag: bool = False + class QueryType(GraphQLObject): + search: str -def test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserInput(InputType): - __schema__ = """ - input User { - id: ID! - } - """ - __args__ = { - "fullName": "full_name", - } + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age, input.flag])}" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String! = "default" + age: Int! = 42 + flag: Boolean! = false + } + """, + ) + + result = graphql_sync(schema, "{ search(input: {}) }") + + assert not result.errors + assert result.data == {"search": "['default', 42, False]"} + + +def test_input_type_with_field_instance_default_value(assert_schema_equals): + class SearchInput(GraphQLInput): + query: str = GraphQLInput.field(default_value="default") + age: int = GraphQLInput.field(default_value=42) + flag: bool = GraphQLInput.field(default_value=False) + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return f"{repr([input.query, input.age, input.flag])}" - data_regression.check(str(err.value)) + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + query: String! = "default" + age: Int! = 42 + flag: Boolean! = false + } + """, + ) + + result = graphql_sync(schema, "{ search(input: {}) }") -class UserInput(InputType): - __schema__ = """ - input UserInput { - id: ID! - fullName: String! - } - """ - __args__ = { - "fullName": "full_name", - } + assert not result.errors + assert result.data == {"search": "['default', 42, False]"} -class GenericScalar(ScalarType): - __schema__ = "scalar Generic" +def test_input_type_with_field_type(assert_schema_equals): + class SearchInput(GraphQLInput): + query: str = GraphQLInput.field(graphql_type=int) + class QueryType(GraphQLObject): + search: str -class QueryType(ObjectType): - __schema__ = """ - type Query { - reprInput(input: UserInput): Generic! - } - """ - __aliases__ = {"reprInput": "repr_input"} - __requires__ = [GenericScalar, UserInput] + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return str(input) - @staticmethod - def resolve_repr_input(*_, input): # pylint: disable=redefined-builtin - return input + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } -schema = make_executable_schema(QueryType) + input SearchInput { + query: Int! + } + """, + ) -def test_input_type_maps_args_to_python_dict_keys(): - result = graphql_sync(schema, '{ reprInput(input: {id: "1", fullName: "Alice"}) }') - assert result.data == { - "reprInput": {"id": "1", "full_name": "Alice"}, - } +def test_schema_input_type_with_field_description(assert_schema_equals): + class SearchInput(GraphQLInput): + __schema__ = """ + input SearchInput { + \"\"\"Hello world.\"\"\" + query: String! + } + """ + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return str(input) + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + \"\"\"Hello world.\"\"\" + query: String! + } + """, + ) + + +def test_input_type_with_field_description(assert_schema_equals): + class SearchInput(GraphQLInput): + query: str = GraphQLInput.field(description="Hello world.") + + class QueryType(GraphQLObject): + search: str + + @GraphQLObject.resolver("search") + def resolve_search(*_, input: SearchInput) -> str: + return str(input) + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + search(input: SearchInput!): String! + } + + input SearchInput { + \"\"\"Hello world.\"\"\" + query: String! + } + """, + ) diff --git a/tests_next/test_input_type_validation.py b/tests/test_input_type_validation.py similarity index 97% rename from tests_next/test_input_type_validation.py rename to tests/test_input_type_validation.py index f1e6273..94ea418 100644 --- a/tests_next/test_input_type_validation.py +++ b/tests/test_input_type_validation.py @@ -1,7 +1,7 @@ import pytest -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import GraphQLInput +from ariadne import gql +from ariadne_graphql_modules import GraphQLInput def test_schema_input_type_validation_fails_for_invalid_type_schema(data_regression): diff --git a/tests/test_interface_type.py b/tests/test_interface_type.py index be3637d..6ff2bbb 100644 --- a/tests/test_interface_type.py +++ b/tests/test_interface_type.py @@ -1,462 +1,369 @@ -from dataclasses import dataclass +from typing import List, Union -import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, graphql_sync +from graphql import graphql_sync from ariadne_graphql_modules import ( - DeferredType, - DirectiveType, - InterfaceType, - ObjectType, + GraphQLID, + GraphQLObject, + GraphQLInterface, + GraphQLUnion, make_executable_schema, ) -def test_interface_type_raises_attribute_error_when_defined_without_schema( - data_regression, -): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - pass +class CommentType(GraphQLObject): + id: GraphQLID + content: str - data_regression.check(str(err.value)) +def test_interface_without_schema(assert_schema_equals): + class UserInterface(GraphQLInterface): + summary: str + score: int -def test_interface_type_raises_error_when_defined_with_invalid_schema_type( - data_regression, -): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = True + class UserType(GraphQLObject, UserInterface): + name: str - data_regression.check(str(err.value)) + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(id=1, username="Bob"), + CommentType(id=2, content="Hello World!"), + ] -def test_interface_type_raises_error_when_defined_with_invalid_schema_str( - data_regression, -): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = "interfaco Example" - - data_regression.check(str(err.value)) + schema = make_executable_schema(QueryType, UserInterface, UserType) + assert_schema_equals( + schema, + """ + type Query { + search: [Result!]! + } -def test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = "type Example" + union Result = User | Comment - data_regression.check(str(err.value)) + type User implements UserInterface { + summary: String! + score: Int! + name: String! + } + type Comment { + id: ID! + content: String! + } -def test_interface_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example + interface UserInterface { + summary: String! + score: Int! + } - interface Other - """ + """, + ) + + +def test_interface_inheritance_without_schema(assert_schema_equals): + def hello_resolver(*_, name: str) -> str: + return f"Hello {name}!" + + class UserInterface(GraphQLInterface): + summary: str + score: str = GraphQLInterface.field( + hello_resolver, + name="better_score", + graphql_type=str, + args={"name": GraphQLInterface.argument(name="json")}, + description="desc", + default_value="my_json", + ) + + class UserType(GraphQLObject, UserInterface): + name: str = GraphQLInterface.field( + name="name", + graphql_type=str, + args={"name": GraphQLInterface.argument(name="json")}, + default_value="my_json", + ) + + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(), + CommentType(id=2, content="Hello World!"), + ] + + schema = make_executable_schema(QueryType, UserInterface, UserType) + + assert_schema_equals( + schema, + """ + type Query { + search: [Result!]! + } - data_regression.check(str(err.value)) + union Result = User | Comment + type User implements UserInterface { + summary: String! -def test_interface_type_raises_error_when_defined_without_fields(data_regression): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = "interface Example" + \"\"\"desc\"\"\" + better_score(json: String!): String! + name: String! + } - data_regression.check(str(err.value)) + type Comment { + id: ID! + content: String! + } + interface UserInterface { + summary: String! -def test_interface_type_extracts_graphql_name(): - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! + \"\"\"desc\"\"\" + better_score(json: String!): String! } - """ - assert ExampleInterface.graphql_name == "Example" + """, + ) + result = graphql_sync( + schema, '{ search { ... on User{ better_score(json: "test") } } }' + ) -def test_interface_type_raises_error_when_defined_without_return_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - group: Group - groups: [Group!] - } - """ + assert not result.errors + assert result.data == {"search": [{"better_score": "Hello test!"}, {}]} - data_regression.check(str(err.value)) - -def test_interface_type_verifies_field_dependency(): - # pylint: disable=unused-variable - class GroupType(ObjectType): +def test_interface_with_schema(assert_schema_equals): + class UserInterface(GraphQLInterface): __schema__ = """ - type Group { - id: ID! + interface UserInterface { + summary: String! + score: Int! } """ - class ExampleInterface(InterfaceType): + class UserType(GraphQLObject): __schema__ = """ - interface Example { - group: Group - groups: [Group!] + type User implements UserInterface { + id: ID! + name: String! + summary: String! + score: Int! } """ - __requires__ = [GroupType] + __implements__ = [UserInterface] -def test_interface_type_verifies_circural_dependency(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - parent: Example - } - """ + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(id=1, username="Bob"), + CommentType(id=2, content="Hello World!"), + ] -def test_interface_type_raises_error_when_defined_without_argument_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - actions(input: UserInput): [String!]! - } - """ + schema = make_executable_schema(QueryType, UserType) - data_regression.check(str(err.value)) + assert_schema_equals( + schema, + """ + type Query { + search: [Result!]! + } + union Result = User | Comment -def test_interface_type_verifies_circular_dependency_using_deferred_type(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! - users: [User] + type User implements UserInterface { + id: ID! + name: String! + summary: String! + score: Int! } - """ - __requires__ = [DeferredType("User")] - class UserType(ObjectType): - __schema__ = """ - type User { - roles: [Example] + interface UserInterface { + summary: String! + score: Int! } - """ - __requires__ = [ExampleInterface] - -def test_interface_type_can_be_extended_with_new_fields(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! + type Comment { + id: ID! + content: String! } + + """, + ) + + +def test_interface_inheritance2(assert_schema_equals): + class BaseEntityInterface(GraphQLInterface): + id: GraphQLID + + class UserInterface(BaseEntityInterface): + username: str + + class UserType(GraphQLObject, UserInterface): + name: str + + class SuperUserType(UserType): + is_super_user: bool + + class QueryType(GraphQLObject): + @GraphQLObject.field + def users(*_) -> List[UserInterface]: + return [ + UserType(id="1", username="test_user"), + SuperUserType( + id="2", + username="test_super_user", + is_super_user=True, + ), + ] + + schema = make_executable_schema( + QueryType, BaseEntityInterface, UserInterface, UserType, SuperUserType + ) + + assert_schema_equals( + schema, """ + type Query { + users: [UserInterface!]! + } - class ExtendExampleInterface(InterfaceType): - __schema__ = """ - extend interface Example { - name: String + interface UserInterface { + id: ID! + username: String! } - """ - __requires__ = [ExampleInterface] + interface BaseEntityInterface { + id: ID! + } -def test_interface_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on INTERFACE" - __visitor__ = SchemaDirectiveVisitor + type User implements BaseEntityInterface & UserInterface { + id: ID! + username: String! + name: String! + } - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! + type SuperUser implements BaseEntityInterface & UserInterface { + id: ID! + username: String! + name: String! + isSuperUser: Boolean! } - """ + """, + ) - class ExtendExampleInterface(InterfaceType): - __schema__ = """ - extend interface Example @example + +def test_interface_descriptions(assert_schema_equals): + class UserInterface(GraphQLInterface): + summary: str + score: int + + __description__ = "Lorem ipsum." + + class UserType(GraphQLObject, UserInterface): + id: GraphQLID + username: str + + class QueryType(GraphQLObject): + @GraphQLObject.field + def user(*_) -> UserType: + return UserType(id="1", username="test_user") + + schema = make_executable_schema(QueryType, UserType, UserInterface) + + assert_schema_equals( + schema, """ - __requires__ = [ExampleInterface, ExampleDirective] + type Query { + user: User! + } + type User implements UserInterface { + summary: String! + score: Int! + id: ID! + username: String! + } -def test_interface_type_can_be_extended_with_other_interface(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! + \"\"\"Lorem ipsum.\"\"\" + interface UserInterface { + summary: String! + score: Int! } + """, + ) + + +def test_interface_resolvers_and_field_descriptions(assert_schema_equals): + class UserInterface(GraphQLInterface): + summary: str + score: int + + @GraphQLInterface.resolver("score", description="Lorem ipsum.") + def resolve_score(*_): + return 200 + + class UserType(GraphQLObject, UserInterface): + id: GraphQLID + + class MyType(GraphQLObject, UserInterface): + id: GraphQLID + name: str + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[UserInterface]) + def users(*_) -> List[UserInterface]: + return [MyType(id="2", name="old", summary="ss", score=22)] + + schema = make_executable_schema(QueryType, UserType, MyType, UserInterface) + + assert_schema_equals( + schema, """ + type Query { + users: [UserInterface!]! + } - class OtherInterface(InterfaceType): - __schema__ = """ - interface Other { - depth: Int! + interface UserInterface { + summary: String! + + \"\"\"Lorem ipsum.\"\"\" + score: Int! } - """ - class ExtendExampleInterface(InterfaceType): - __schema__ = """ - extend interface Example implements Other - """ - __requires__ = [ExampleInterface, OtherInterface] - - -def test_interface_type_raises_error_when_defined_without_extended_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExtendExampleInterface(ObjectType): - __schema__ = """ - extend interface Example { - name: String - } - """ - - data_regression.check(str(err.value)) - - -def test_interface_type_raises_error_when_extended_dependency_is_wrong_type( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleType(ObjectType): - __schema__ = """ - type Example { - id: ID! - } - """ - - class ExampleInterface(InterfaceType): - __schema__ = """ - extend interface Example { - name: String - } - """ - __requires__ = [ExampleType] - - data_regression.check(str(err.value)) - - -def test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface User { - name: String - } - """ - __aliases__ = { - "joinedDate": "joined_date", - } - - data_regression.check(str(err.value)) - - -def test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface User { - name: String - } - """ - - @staticmethod - def resolve_group(*_): - return None - - data_regression.check(str(err.value)) - - -@dataclass -class User: - id: int - name: str - summary: str - - -@dataclass -class Comment: - id: int - message: str - summary: str - - -class ResultInterface(InterfaceType): - __schema__ = """ - interface Result { - summary: String! - score: Int! - } - """ - - @staticmethod - def resolve_type(instance, *_): - if isinstance(instance, Comment): - return "Comment" - - if isinstance(instance, User): - return "User" - - return None - - @staticmethod - def resolve_score(*_): - return 42 - - -class UserType(ObjectType): - __schema__ = """ - type User implements Result { - id: ID! - name: String! - summary: String! - score: Int! - } - """ - __requires__ = [ResultInterface] - - -class CommentType(ObjectType): - __schema__ = """ - type Comment implements Result { - id: ID! - message: String! - summary: String! - score: Int! - } - """ - __requires__ = [ResultInterface] - - @staticmethod - def resolve_score(*_): - return 16 - - -class QueryType(ObjectType): - __schema__ = """ - type Query { - results: [Result!]! - } - """ - __requires__ = [ResultInterface] - - @staticmethod - def resolve_results(*_): - return [ - User(id=1, name="Alice", summary="Summary for Alice"), - Comment(id=1, message="Hello world!", summary="Summary for comment"), - ] - - -schema = make_executable_schema(QueryType, UserType, CommentType) - - -def test_interface_type_binds_type_resolver(): - query = """ - query { - results { - ... on User { - __typename - id - name - summary - } - ... on Comment { - __typename - id - message - summary - } + type User implements UserInterface { + summary: String! + + \"\"\"Lorem ipsum.\"\"\" + score: Int! + id: ID! } - } - """ - - result = graphql_sync(schema, query) - assert result.data == { - "results": [ - { - "__typename": "User", - "id": "1", - "name": "Alice", - "summary": "Summary for Alice", - }, - { - "__typename": "Comment", - "id": "1", - "message": "Hello world!", - "summary": "Summary for comment", - }, - ], - } - - -def test_interface_type_binds_field_resolvers_to_implementing_types_fields(): - query = """ - query { - results { - ... on User { - __typename - score - } - ... on Comment { - __typename - score - } + + type My implements UserInterface { + summary: String! + + \"\"\"Lorem ipsum.\"\"\" + score: Int! + id: ID! + name: String! } - } - """ - - result = graphql_sync(schema, query) - assert result.data == { - "results": [ - { - "__typename": "User", - "score": 42, - }, - { - "__typename": "Comment", - "score": 16, - }, - ], - } + """, + ) + result = graphql_sync(schema, "{ users { ... on My { __typename score } } }") + + assert not result.errors + assert result.data == {"users": [{"__typename": "My", "score": 200}]} diff --git a/tests/test_interface_type_validation.py b/tests/test_interface_type_validation.py new file mode 100644 index 0000000..d441080 --- /dev/null +++ b/tests/test_interface_type_validation.py @@ -0,0 +1,23 @@ +import pytest + +from ariadne_graphql_modules import ( + GraphQLID, + GraphQLObject, + GraphQLInterface, + make_executable_schema, +) + + +def test_interface_no_interface_in_schema(data_regression): + with pytest.raises(TypeError) as exc_info: + + class BaseInterface(GraphQLInterface): + id: GraphQLID + + class UserType(GraphQLObject, BaseInterface): + username: str + email: str + + make_executable_schema(UserType) + + data_regression.check(str(exc_info.value)) diff --git a/tests_next/test_make_executable_schema.py b/tests/test_make_executable_schema.py similarity index 98% rename from tests_next/test_make_executable_schema.py rename to tests/test_make_executable_schema.py index b927649..635901b 100644 --- a/tests_next/test_make_executable_schema.py +++ b/tests/test_make_executable_schema.py @@ -10,7 +10,7 @@ ) from ariadne import QueryType, SchemaDirectiveVisitor -from ariadne_graphql_modules.next import GraphQLObject, make_executable_schema +from ariadne_graphql_modules import GraphQLObject, make_executable_schema def test_executable_schema_from_vanilla_schema_definition(assert_schema_equals): diff --git a/tests_next/test_metadata.py b/tests/test_metadata.py similarity index 96% rename from tests_next/test_metadata.py rename to tests/test_metadata.py index d02f5f6..443d022 100644 --- a/tests_next/test_metadata.py +++ b/tests/test_metadata.py @@ -2,7 +2,7 @@ import pytest -from ariadne_graphql_modules.next import GraphQLObject, graphql_enum +from ariadne_graphql_modules import GraphQLObject, graphql_enum class QueryType(GraphQLObject): diff --git a/tests/test_object_type.py b/tests/test_object_type.py index 8adf0e6..1d5f247 100644 --- a/tests/test_object_type.py +++ b/tests/test_object_type.py @@ -1,410 +1,1015 @@ +from typing import Optional + import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, graphql_sync +from graphql import graphql_sync -from ariadne_graphql_modules import ( - DeferredType, - DirectiveType, - InterfaceType, - ObjectType, - make_executable_schema, -) +from ariadne import gql +from ariadne_graphql_modules import GraphQLObject, make_executable_schema -def test_object_type_raises_attribute_error_when_defined_without_schema( - data_regression, -): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - pass +def test_object_type_instance_with_all_attrs_values(): + class CategoryType(GraphQLObject): + name: str + posts: int - data_regression.check(str(err.value)) + obj = CategoryType(name="Welcome", posts=20) + assert obj.name == "Welcome" + assert obj.posts == 20 -def test_object_type_raises_error_when_defined_with_invalid_schema_type( - data_regression, -): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = True +def test_object_type_instance_with_omitted_attrs_being_none(): + class CategoryType(GraphQLObject): + name: str + posts: int - data_regression.check(str(err.value)) + obj = CategoryType(posts=20) + assert obj.name is None + assert obj.posts == 20 -def test_object_type_raises_error_when_defined_with_invalid_schema_str(data_regression): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = "typo User" +def test_object_type_instance_with_aliased_attrs_values(): + class CategoryType(GraphQLObject): + name: str + posts: int - data_regression.check(str(err.value)) + __aliases__ = {"name": "title"} + title: str -def test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = "scalar DateTime" + obj = CategoryType(title="Welcome", posts=20) + assert obj.title == "Welcome" + assert obj.posts == 20 - data_regression.check(str(err.value)) +def test_object_type_instance_with_omitted_attrs_being_default_values(): + class CategoryType(GraphQLObject): + name: str = "Hello" + posts: int = 42 -def test_object_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User + obj = CategoryType(posts=20) + assert obj.name == "Hello" + assert obj.posts == 20 + + +def test_object_type_instance_with_all_attrs_being_default_values(): + class CategoryType(GraphQLObject): + name: str = "Hello" + posts: int = 42 + + obj = CategoryType() + assert obj.name == "Hello" + assert obj.posts == 42 + + +def test_object_type_instance_with_invalid_attrs_raising_error(data_regression): + class CategoryType(GraphQLObject): + name: str + posts: int - type Group + with pytest.raises(TypeError) as exc_info: + CategoryType(name="Welcome", invalid="Ok") + + data_regression.check(str(exc_info.value)) + + +def test_schema_object_type_instance_with_all_attrs_values(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } """ + ) + + name: str + posts: int - data_regression.check(str(err.value)) + obj = CategoryType(name="Welcome", posts=20) + assert obj.name == "Welcome" + assert obj.posts == 20 -def test_object_type_raises_error_when_defined_without_fields(data_regression): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = "type User" +def test_schema_object_type_instance_with_omitted_attrs_being_none(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } + """ + ) - data_regression.check(str(err.value)) + name: str + posts: int + obj = CategoryType(posts=20) + assert obj.name is None + assert obj.posts == 20 -def test_object_type_extracts_graphql_name(): - class GroupType(ObjectType): - __schema__ = """ - type Group { - id: ID! - } - """ - assert GroupType.graphql_name == "Group" +def test_schema_object_type_instance_with_omitted_attrs_being_default_values(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } + """ + ) + name: str = "Hello" + posts: int = 42 -def test_object_type_accepts_all_builtin_scalar_types(): - # pylint: disable=unused-variable - class FancyObjectType(ObjectType): - __schema__ = """ - type FancyObject { - id: ID! - someInt: Int! - someFloat: Float! - someBoolean: Boolean! - someString: String! - } - """ + obj = CategoryType(posts=20) + assert obj.name == "Hello" + assert obj.posts == 20 -def test_object_type_raises_error_when_defined_without_return_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - group: Group - groups: [Group!] +def test_schema_object_type_instance_with_all_attrs_being_default_values(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int } """ + ) - data_regression.check(str(err.value)) + name: str = "Hello" + posts: int = 42 + obj = CategoryType() + assert obj.name == "Hello" + assert obj.posts == 42 -def test_object_type_verifies_field_dependency(): - # pylint: disable=unused-variable - class GroupType(ObjectType): - __schema__ = """ - type Group { - id: ID! - } - """ - class UserType(ObjectType): - __schema__ = """ - type User { - group: Group - groups: [Group!] - } - """ - __requires__ = [GroupType] +def test_schema_object_type_instance_with_aliased_attrs_values(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } + """ + ) + __aliases__ = {"name": "title"} + title: str = "Hello" + posts: int = 42 -def test_object_type_verifies_circular_dependency(): - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - follows: User - } - """ + obj = CategoryType(title="Ok") + assert obj.title == "Ok" + assert obj.posts == 42 -def test_object_type_raises_error_when_defined_without_argument_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - actions(input: UserInput): [String!]! +def test_schema_object_type_instance_with_aliased_attrs_default_values(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int } """ + ) + __aliases__ = {"name": "title"} - data_regression.check(str(err.value)) + title: str = "Hello" + posts: int = 42 + obj = CategoryType() + assert obj.title == "Hello" + assert obj.posts == 42 -def test_object_type_verifies_circular_dependency_using_deferred_type(): - # pylint: disable=unused-variable - class GroupType(ObjectType): - __schema__ = """ - type Group { - id: ID! - users: [User] - } + +def test_schema_object_type_instance_with_invalid_attrs_raising_error(data_regression): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } + """ + ) + + name: str + posts: int + + with pytest.raises(TypeError) as exc_info: + CategoryType(name="Welcome", invalid="Ok") + + data_regression.check(str(exc_info.value)) + + +def test_schema_object_type_instance_with_aliased_attr_value(): + class CategoryType(GraphQLObject): + __schema__ = gql( + """ + type Category { + name: String + posts: Int + } + """ + ) + __aliases__ = {"name": "title"} + + title: str + posts: int + + obj = CategoryType(title="Welcome", posts=20) + assert obj.title == "Welcome" + assert obj.posts == 20 + + +def test_object_type_with_field(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [DeferredType("User")] + type Query { + hello: String! + } + """, + ) - class UserType(ObjectType): - __schema__ = """ - type User { - group: Group + result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_object_type_with_alias(assert_schema_equals): + class QueryType(GraphQLObject): + __aliases__ = {"hello": "welcome_message"} + + hello: str + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello: String! } + """, + ) + + result = graphql_sync( + schema, "{ hello }", root_value={"welcome_message": "Hello World!"} + ) + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_object_type_with_alias_excludes_alias_targets(assert_schema_equals): + class QueryType(GraphQLObject): + __aliases__ = {"hello": "welcome"} + + hello: str + welcome: str + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [GroupType] + type Query { + hello: String! + } + """, + ) + result = graphql_sync(schema, "{ hello }", root_value={"welcome": "Hello World!"}) -def test_object_type_can_be_extended_with_new_fields(): - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - id: ID! + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_object_type_with_alias_includes_aliased_field_instances(assert_schema_equals): + class QueryType(GraphQLObject): + __aliases__ = {"hello": "welcome"} + + hello: str + welcome: str = GraphQLObject.field() + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello: String! + welcome: String! } + """, + ) + + result = graphql_sync( + schema, "{ hello welcome }", root_value={"welcome": "Hello World!"} + ) + + assert not result.errors + assert result.data == {"hello": "Hello World!", "welcome": "Hello World!"} + + +def test_object_type_with_attr_automatic_alias(assert_schema_equals): + class QueryType(GraphQLObject): + test_message: str + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + testMessage: String! + } + """, + ) - class ExtendUserType(ObjectType): - __schema__ = """ - extend type User { - name: String + result = graphql_sync( + schema, "{ testMessage }", root_value={"test_message": "Hello World!"} + ) + + assert not result.errors + assert result.data == {"testMessage": "Hello World!"} + + +def test_object_type_with_field_instance_automatic_alias(assert_schema_equals): + class QueryType(GraphQLObject): + message: str = GraphQLObject.field(name="testMessage") + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + testMessage: String! } + """, + ) + + result = graphql_sync( + schema, "{ testMessage }", root_value={"message": "Hello World!"} + ) + + assert not result.errors + assert result.data == {"testMessage": "Hello World!"} + + +def test_object_type_with_field_resolver(assert_schema_equals): + class QueryType(GraphQLObject): + @GraphQLObject.field + def hello(obj, info) -> str: + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [UserType] + type Query { + hello: String! + } + """, + ) + result = graphql_sync(schema, "{ hello }") -def test_object_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on OBJECT" - __visitor__ = SchemaDirectiveVisitor + assert not result.errors + assert result.data == {"hello": "Hello World!"} - class UserType(ObjectType): - __schema__ = """ - type User { - id: ID! + +def test_object_type_with_typed_field_instance(assert_schema_equals): + class QueryType(GraphQLObject): + hello = GraphQLObject.field(lambda *_: "Hello World!", graphql_type=str) + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello: String! } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_object_type_with_annotated_field_instance(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str = GraphQLObject.field(lambda *_: "Hello World!") + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} - class ExtendUserType(ObjectType): - __schema__ = """ - extend type User @example + +def test_object_type_with_typed_field_and_field_resolver(assert_schema_equals): + class QueryType(GraphQLObject): + name: str + + @GraphQLObject.field + def hello(obj, info) -> str: + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [UserType, ExampleDirective] + type Query { + name: String! + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ name hello }", root_value={"name": "Ok"}) + + assert not result.errors + assert result.data == {"name": "Ok", "hello": "Hello World!"} -def test_object_type_can_be_extended_with_interface(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Interface { - id: ID! +def test_object_type_with_schema(assert_schema_equals): + class QueryType(GraphQLObject): + __schema__ = gql( + """ + type Query { + hello: String! + } + """ + ) + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello: String! } + """, + ) + + result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_object_type_with_nested_types(assert_schema_equals): + class UserType(GraphQLObject): + name: str + + class PostType(GraphQLObject): + message: str + + class QueryType(GraphQLObject): + user: UserType + + @GraphQLObject.field(graphql_type=PostType) + def post(obj, info): + return {"message": "test"} + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + user: User! + post: Post! + } - class UserType(ObjectType): - __schema__ = """ type User { - id: ID! + name: String! + } + + type Post { + message: String! } + """, + ) + + result = graphql_sync( + schema, + "{ user { name } post { message } }", + root_value={"user": {"name": "Bob"}}, + ) + + assert not result.errors + assert result.data == { + "user": { + "name": "Bob", + }, + "post": {"message": "test"}, + } + + +def test_resolver_decorator_sets_resolver_for_type_hint_field(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str + + @GraphQLObject.resolver("hello") + def resolve_hello(*_): + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ + type Query { + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_resolver_decorator_sets_resolver_for_instance_field(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str = GraphQLObject.field(name="hello") - class ExtendUserType(ObjectType): - __schema__ = """ - extend type User implements Interface + @GraphQLObject.resolver("hello") + def resolve_hello(*_): + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, """ - __requires__ = [UserType, ExampleInterface] + type Query { + hello: String! + } + """, + ) + result = graphql_sync(schema, "{ hello }") -def test_object_type_raises_error_when_defined_without_extended_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExtendUserType(ObjectType): - __schema__ = """ - extend type User { - name: String + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_resolver_decorator_sets_resolver_for_field_in_schema(assert_schema_equals): + class QueryType(GraphQLObject): + __schema__ = gql( + """ + type Query { + hello: String! } """ + ) - data_regression.check(str(err.value)) + @GraphQLObject.resolver("hello") + def resolve_hello(*_): + return "Hello World!" + schema = make_executable_schema(QueryType) -def test_object_type_raises_error_when_extended_dependency_is_wrong_type( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Example { - id: ID! - } - """ + assert_schema_equals( + schema, + """ + type Query { + hello: String! + } + """, + ) - class ExampleType(ObjectType): - __schema__ = """ - extend type Example { - name: String - } - """ - __requires__ = [ExampleInterface] + result = graphql_sync(schema, "{ hello }") - data_regression.check(str(err.value)) + assert not result.errors + assert result.data == {"hello": "Hello World!"} -def test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - name: String - } +def test_object_type_with_description(assert_schema_equals): + class QueryType(GraphQLObject): + __description__ = "Lorem ipsum." + + hello: str + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + \"\"\"Lorem ipsum.\"\"\" + type Query { + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_field_decorator_sets_description_for_field(assert_schema_equals): + class QueryType(GraphQLObject): + @GraphQLObject.field(description="Lorem ipsum.") + def hello(obj, info) -> str: + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + \"\"\"Lorem ipsum.\"\"\" + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_field_decorator_sets_description_for_field_arg(assert_schema_equals): + class QueryType(GraphQLObject): + @GraphQLObject.field( + args={"name": GraphQLObject.argument(description="Lorem ipsum.")} + ) + def hello(obj, info, name: str) -> str: + return f"Hello {name}!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello( + \"\"\"Lorem ipsum.\"\"\" + name: String! + ): String! + } + """, + ) + + result = graphql_sync(schema, '{ hello(name: "Bob") }') + + assert not result.errors + assert result.data == {"hello": "Hello Bob!"} + + +def test_resolver_decorator_sets_description_for_type_hint_field(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str + + @GraphQLObject.resolver("hello", description="Lorem ipsum.") + def resolve_hello(*_): + return "Hello World!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + \"\"\"Lorem ipsum.\"\"\" + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_resolver_decorator_sets_description_for_field_in_schema(assert_schema_equals): + class QueryType(GraphQLObject): + __schema__ = gql( """ - __aliases__ = { - "joinedDate": "joined_date", + type Query { + hello: String! } + """ + ) - data_regression.check(str(err.value)) + @GraphQLObject.resolver("hello", description="Lorem ipsum.") + def resolve_hello(*_): + return "Hello World!" + schema = make_executable_schema(QueryType) -def test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - name: String + assert_schema_equals( + schema, + """ + type Query { + \"\"\"Lorem ipsum.\"\"\" + hello: String! + } + """, + ) + + result = graphql_sync(schema, "{ hello }") + + assert not result.errors + assert result.data == {"hello": "Hello World!"} + + +def test_resolver_decorator_sets_description_for_field_arg(assert_schema_equals): + class QueryType(GraphQLObject): + hello: str + + @GraphQLObject.resolver( + "hello", args={"name": GraphQLObject.argument(description="Lorem ipsum.")} + ) + def resolve_hello(obj, info, name: str) -> str: + return f"Hello {name}!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello( + \"\"\"Lorem ipsum.\"\"\" + name: String! + ): String! + } + """, + ) + + result = graphql_sync(schema, '{ hello(name: "Bob") }') + + assert not result.errors + assert result.data == {"hello": "Hello Bob!"} + + +def test_schema_sets_description_for_field_arg(assert_schema_equals): + class QueryType(GraphQLObject): + __schema__ = gql( + """ + type Query { + hello( + \"\"\"Lorem ipsum.\"\"\" + name: String! + ): String! } """ + ) - @staticmethod - def resolve_group(*_): - return None + @GraphQLObject.resolver("hello") + def resolve_hello(*_, name: str): + return f"Hello {name}!" - data_regression.check(str(err.value)) + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + type Query { + hello( + \"\"\"Lorem ipsum.\"\"\" + name: String! + ): String! + } + """, + ) -def test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field( - data_regression, + result = graphql_sync(schema, '{ hello(name: "Bob") }') + + assert not result.errors + assert result.data == {"hello": "Hello Bob!"} + + +def test_resolver_decorator_sets_description_for_field_arg_in_schema( + assert_schema_equals, ): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - name: String + class QueryType(GraphQLObject): + __schema__ = gql( + """ + type Query { + hello(name: String!): String! } """ - __fields_args__ = {"group": {}} + ) + + @GraphQLObject.resolver( + "hello", args={"name": GraphQLObject.argument(description="Description")} + ) + def resolve_hello(*_, name: str): + return f"Hello {name}!" + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + hello( + \"\"\"Description\"\"\" + name: String! + ): String! + } + """, + ) + + result = graphql_sync(schema, '{ hello(name: "Bob") }') - data_regression.check(str(err.value)) + assert not result.errors + assert result.data == {"hello": "Hello Bob!"} -def test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg( - data_regression, +def test_object_type_self_reference( + assert_schema_equals, ): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UserType(ObjectType): - __schema__ = """ - type User { - name: String - } - """ - __fields_args__ = {"name": {"arg": "arg2"}} + class CategoryType(GraphQLObject): + name: str + parent: Optional["CategoryType"] - data_regression.check(str(err.value)) + class QueryType(GraphQLObject): + category: CategoryType + schema = make_executable_schema(QueryType) -class QueryType(ObjectType): - __schema__ = """ - type Query { - field: String! - other: String! - firstField: String! - secondField: String! - fieldWithArg(someArg: String): String! - } - """ - __aliases__ = { - "firstField": "first_field", - "secondField": "second_field", - "fieldWithArg": "field_with_arg", + assert_schema_equals( + schema, + """ + type Query { + category: Category! + } + + type Category { + name: String! + parent: Category + } + """, + ) + + result = graphql_sync( + schema, + "{ category { name parent { name } } }", + root_value={ + "category": { + "name": "Lorem", + "parent": { + "name": "Ipsum", + }, + }, + }, + ) + + assert not result.errors + assert result.data == { + "category": { + "name": "Lorem", + "parent": { + "name": "Ipsum", + }, + }, } - __fields_args__ = {"fieldWithArg": {"someArg": "some_arg"}} - @staticmethod - def resolve_other(*_): - return "Word Up!" - @staticmethod - def resolve_second_field(obj, *_): - return "Obj: %s" % obj["secondField"] +def test_object_type_return_instance( + assert_schema_equals, +): + class CategoryType(GraphQLObject): + name: str + color: str + + class QueryType(GraphQLObject): + @GraphQLObject.field() + def category(*_) -> CategoryType: + return CategoryType( + name="Welcome", + color="#FF00FF", + ) + + schema = make_executable_schema(QueryType) + + assert_schema_equals( + schema, + """ + type Query { + category: Category! + } - @staticmethod - def resolve_field_with_arg(*_, some_arg): - return some_arg + type Category { + name: String! + color: String! + } + """, + ) + result = graphql_sync(schema, "{ category { name color } }") -schema = make_executable_schema(QueryType) + assert not result.errors + assert result.data == { + "category": { + "name": "Welcome", + "color": "#FF00FF", + }, + } -def test_object_resolves_field_with_default_resolver(): - result = graphql_sync(schema, "{ field }", root_value={"field": "Hello!"}) - assert result.data["field"] == "Hello!" +def test_object_type_nested_type( + assert_schema_equals, +): + class UserType(GraphQLObject): + username: str + class CategoryType(GraphQLObject): + name: str + parent: Optional["CategoryType"] + owner: UserType -def test_object_resolves_field_with_custom_resolver(): - result = graphql_sync(schema, "{ other }") - assert result.data["other"] == "Word Up!" + class QueryType(GraphQLObject): + category: CategoryType + schema = make_executable_schema(QueryType) -def test_object_resolves_field_with_aliased_default_resolver(): - result = graphql_sync( - schema, "{ firstField }", root_value={"first_field": "Howdy?"} - ) - assert result.data["firstField"] == "Howdy?" + assert_schema_equals( + schema, + """ + type Query { + category: Category! + } + type Category { + name: String! + parent: Category + owner: User! + } -def test_object_resolves_field_with_aliased_custom_resolver(): - result = graphql_sync(schema, "{ secondField }", root_value={"secondField": "Hey!"}) - assert result.data["secondField"] == "Obj: Hey!" + type User { + username: String! + } + """, + ) + result = graphql_sync( + schema, + "{ category { name parent { name } owner { username } } }", + root_value={ + "category": { + "name": "Lorem", + "parent": { + "name": "Ipsum", + }, + "owner": { + "username": "John", + }, + }, + }, + ) -def test_object_resolves_field_with_arg_out_name_customized(): - result = graphql_sync(schema, '{ fieldWithArg(someArg: "test") }') - assert result.data["fieldWithArg"] == "test" + assert not result.errors + assert result.data == { + "category": { + "name": "Lorem", + "parent": { + "name": "Ipsum", + }, + "owner": { + "username": "John", + }, + }, + } diff --git a/tests_next/test_object_type_field_args.py b/tests/test_object_type_field_args.py similarity index 99% rename from tests_next/test_object_type_field_args.py rename to tests/test_object_type_field_args.py index 2a358cb..10e815b 100644 --- a/tests_next/test_object_type_field_args.py +++ b/tests/test_object_type_field_args.py @@ -1,6 +1,6 @@ from graphql import GraphQLResolveInfo, graphql_sync -from ariadne_graphql_modules.next import GraphQLObject, make_executable_schema +from ariadne_graphql_modules import GraphQLObject, make_executable_schema def test_object_type_field_resolver_with_scalar_arg(assert_schema_equals): diff --git a/tests_next/test_object_type_validation.py b/tests/test_object_type_validation.py similarity index 99% rename from tests_next/test_object_type_validation.py rename to tests/test_object_type_validation.py index 2fcf813..4396a51 100644 --- a/tests_next/test_object_type_validation.py +++ b/tests/test_object_type_validation.py @@ -1,7 +1,7 @@ import pytest -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import GraphQLObject +from ariadne import gql +from ariadne_graphql_modules import GraphQLObject def test_schema_object_type_validation_fails_for_invalid_type_schema(data_regression): diff --git a/tests/test_scalar_type.py b/tests/test_scalar_type.py index ec85340..a880d5b 100644 --- a/tests/test_scalar_type.py +++ b/tests/test_scalar_type.py @@ -1,287 +1,111 @@ -from datetime import date, datetime +from datetime import date -import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, StringValueNode, graphql_sync +from graphql import graphql_sync +from ariadne import gql from ariadne_graphql_modules import ( - DirectiveType, - ObjectType, - ScalarType, + GraphQLObject, + GraphQLScalar, make_executable_schema, ) -def test_scalar_type_raises_attribute_error_when_defined_without_schema( - data_regression, -): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class DateScalar(ScalarType): - pass +class DateScalar(GraphQLScalar[date]): + @classmethod + def serialize(cls, value): + if isinstance(value, cls): + return str(value.unwrap()) - data_regression.check(str(err.value)) + return str(value) -def test_scalar_type_raises_error_when_defined_with_invalid_schema_type( - data_regression, -): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class DateScalar(ScalarType): - __schema__ = True +def test_scalar_field_returning_scalar_instance(assert_schema_equals): + class QueryType(GraphQLObject): + date: DateScalar - data_regression.check(str(err.value)) + @GraphQLObject.resolver("date") + def resolve_date(*_) -> DateScalar: + return DateScalar(date(1989, 10, 30)) + schema = make_executable_schema(QueryType) -def test_scalar_type_raises_error_when_defined_with_invalid_schema_str(data_regression): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class DateScalar(ScalarType): - __schema__ = "scalor Date" - - data_regression.check(str(err.value)) - - -def test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class DateScalar(ScalarType): - __schema__ = "type DateTime" - - data_regression.check(str(err.value)) - - -def test_scalar_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class DateScalar(ScalarType): - __schema__ = """ - scalar Date - - scalar DateTime - """ - - data_regression.check(str(err.value)) - - -def test_scalar_type_extracts_graphql_name(): - class DateScalar(ScalarType): - __schema__ = "scalar Date" - - assert DateScalar.graphql_name == "Date" - - -def test_scalar_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on SCALAR" - __visitor__ = SchemaDirectiveVisitor - - class DateScalar(ScalarType): - __schema__ = "scalar Date" - - class ExtendDateScalar(ScalarType): - __schema__ = "extend scalar Date @example" - __requires__ = [DateScalar, ExampleDirective] - - -class DateReadOnlyScalar(ScalarType): - __schema__ = "scalar DateReadOnly" - - @staticmethod - def serialize(date): - return date.strftime("%Y-%m-%d") - - -class DateInputScalar(ScalarType): - __schema__ = "scalar DateInput" - - @staticmethod - def parse_value(formatted_date): - parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") - return parsed_datetime.date() - - @staticmethod - def parse_literal(ast, variable_values=None): # pylint: disable=unused-argument - if not isinstance(ast, StringValueNode): - raise ValueError() - - formatted_date = ast.value - parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") - return parsed_datetime.date() - - -class DefaultParserScalar(ScalarType): - __schema__ = "scalar DefaultParser" - - @staticmethod - def parse_value(value): - return type(value).__name__ - - -TEST_DATE = date(2006, 9, 13) -TEST_DATE_SERIALIZED = TEST_DATE.strftime("%Y-%m-%d") - - -class QueryType(ObjectType): - __schema__ = """ - type Query { - testSerialize: DateReadOnly! - testInput(value: DateInput!): Boolean! - testInputValueType(value: DefaultParser!): String! - } - """ - __requires__ = [ - DateReadOnlyScalar, - DateInputScalar, - DefaultParserScalar, - ] - __aliases__ = { - "testSerialize": "test_serialize", - "testInput": "test_input", - "testInputValueType": "test_input_value_type", - } - - @staticmethod - def resolve_test_serialize(*_): - return TEST_DATE - - @staticmethod - def resolve_test_input(*_, value): - assert value == TEST_DATE - return True - - @staticmethod - def resolve_test_input_value_type(*_, value): - return value - - -schema = make_executable_schema(QueryType) - - -def test_attempt_deserialize_str_literal_without_valid_date_raises_error(): - test_input = "invalid string" - result = graphql_sync(schema, '{ testInput(value: "%s") }' % test_input) - assert result.errors is not None - assert str(result.errors[0]).splitlines()[:1] == [ - "Expected value of type 'DateInput!', found \"invalid string\"; " - "time data 'invalid string' does not match format '%Y-%m-%d'" - ] - - -def test_attempt_deserialize_wrong_type_literal_raises_error(): - test_input = 123 - result = graphql_sync(schema, "{ testInput(value: %s) }" % test_input) - assert result.errors is not None - assert str(result.errors[0]).splitlines()[:1] == [ - "Expected value of type 'DateInput!', found 123; " - ] - - -def test_default_literal_parser_is_used_to_extract_value_str_from_ast_node(): - class ValueParserOnlyScalar(ScalarType): - __schema__ = "scalar DateInput" - - @staticmethod - def parse_value(formatted_date): - parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") - return parsed_datetime.date() + assert_schema_equals( + schema, + """ + scalar Date - class ValueParserOnlyQueryType(ObjectType): - __schema__ = """ type Query { - parse(value: DateInput!): String! + date: Date! } - """ - __requires__ = [ValueParserOnlyScalar] - - @staticmethod - def resolve_parse(*_, value): - return value - - schema = make_executable_schema(ValueParserOnlyQueryType) - result = graphql_sync(schema, """{ parse(value: "%s") }""" % TEST_DATE_SERIALIZED) - assert result.errors is None - assert result.data == {"parse": "2006-09-13"} + """, + ) + result = graphql_sync(schema, "{ date }") -parametrized_query = """ - query parseValueTest($value: DateInput!) { - testInput(value: $value) - } -""" + assert not result.errors + assert result.data == {"date": "1989-10-30"} -def test_variable_with_valid_date_string_is_deserialized_to_python_date(): - variables = {"value": TEST_DATE_SERIALIZED} - result = graphql_sync(schema, parametrized_query, variable_values=variables) - assert result.errors is None - assert result.data == {"testInput": True} +def test_scalar_field_returning_scalar_wrapped_type(assert_schema_equals): + class QueryType(GraphQLObject): + scalar_date: DateScalar + @GraphQLObject.resolver("scalar_date", graphql_type=DateScalar) + def resolve_date(*_) -> date: + return date(1989, 10, 30) -def test_attempt_deserialize_str_variable_without_valid_date_raises_error(): - variables = {"value": "invalid string"} - result = graphql_sync(schema, parametrized_query, variable_values=variables) - assert result.errors is not None - assert str(result.errors[0]).splitlines()[:1] == [ - "Variable '$value' got invalid value 'invalid string'; " - "Expected type 'DateInput'. " - "time data 'invalid string' does not match format '%Y-%m-%d'" - ] + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + scalar Date -def test_attempt_deserialize_wrong_type_variable_raises_error(): - variables = {"value": 123} - result = graphql_sync(schema, parametrized_query, variable_values=variables) - assert result.errors is not None - assert str(result.errors[0]).splitlines()[:1] == [ - "Variable '$value' got invalid value 123; Expected type 'DateInput'. " - "strptime() argument 1 must be str, not int" - ] + type Query { + scalarDate: Date! + } + """, + ) + result = graphql_sync(schema, "{ scalarDate }") -def test_literal_string_is_deserialized_by_default_parser(): - result = graphql_sync(schema, '{ testInputValueType(value: "test") }') - assert result.errors is None - assert result.data == {"testInputValueType": "str"} + assert not result.errors + assert result.data == {"scalarDate": "1989-10-30"} -def test_literal_int_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: 123) }") - assert result.errors is None - assert result.data == {"testInputValueType": "int"} +class SchemaDateScalar(GraphQLScalar[date]): + __schema__ = gql("scalar Date") + @classmethod + def serialize(cls, value): + if isinstance(value, cls): + return str(value.unwrap()) -def test_literal_float_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: 1.5) }") - assert result.errors is None - assert result.data == {"testInputValueType": "float"} + return str(value) -def test_literal_bool_true_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: true) }") - assert result.errors is None - assert result.data == {"testInputValueType": "bool"} +def test_schema_scalar_field_returning_scalar_instance(assert_schema_equals): + class QueryType(GraphQLObject): + date: SchemaDateScalar + @GraphQLObject.resolver("date") + def resolve_date(*_) -> SchemaDateScalar: + return SchemaDateScalar(date(1989, 10, 30)) -def test_literal_bool_false_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: false) }") - assert result.errors is None - assert result.data == {"testInputValueType": "bool"} + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + scalar Date -def test_literal_object_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: {}) }") - assert result.errors is None - assert result.data == {"testInputValueType": "dict"} + type Query { + date: Date! + } + """, + ) + result = graphql_sync(schema, "{ date }") -def test_literal_list_is_deserialized_by_default_parser(): - result = graphql_sync(schema, "{ testInputValueType(value: []) }") - assert result.errors is None - assert result.data == {"testInputValueType": "list"} + assert not result.errors + assert result.data == {"date": "1989-10-30"} diff --git a/tests_next/test_scalar_type_validation.py b/tests/test_scalar_type_validation.py similarity index 91% rename from tests_next/test_scalar_type_validation.py rename to tests/test_scalar_type_validation.py index d473cb7..edd193e 100644 --- a/tests_next/test_scalar_type_validation.py +++ b/tests/test_scalar_type_validation.py @@ -1,7 +1,7 @@ import pytest -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import GraphQLScalar +from ariadne import gql +from ariadne_graphql_modules import GraphQLScalar def test_schema_scalar_type_validation_fails_for_invalid_type_schema(data_regression): diff --git a/tests_next/test_standard_enum.py b/tests/test_standard_enum.py similarity index 99% rename from tests_next/test_standard_enum.py rename to tests/test_standard_enum.py index 9f8eded..ca95562 100644 --- a/tests_next/test_standard_enum.py +++ b/tests/test_standard_enum.py @@ -3,7 +3,7 @@ import pytest from graphql import graphql_sync -from ariadne_graphql_modules.next import ( +from ariadne_graphql_modules import ( GraphQLObject, create_graphql_enum_model, graphql_enum, diff --git a/tests/test_subscription_type.py b/tests/test_subscription_type.py index c0067cf..bb43729 100644 --- a/tests/test_subscription_type.py +++ b/tests/test_subscription_type.py @@ -1,325 +1,754 @@ -import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, build_schema +from typing import List +from ariadne import gql +from graphql import subscribe, parse +import pytest from ariadne_graphql_modules import ( - DirectiveType, - InterfaceType, - ObjectType, - SubscriptionType, + GraphQLID, + GraphQLObject, + GraphQLSubscription, + GraphQLUnion, + make_executable_schema, ) -def test_subscription_type_raises_attribute_error_when_defined_without_schema( - data_regression, -): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - pass +class Message(GraphQLObject): + id: GraphQLID + content: str + author: str - data_regression.check(str(err.value)) +class User(GraphQLObject): + id: GraphQLID + username: str -def test_subscription_type_raises_error_when_defined_with_invalid_schema_type( - data_regression, -): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - __schema__ = True - data_regression.check(str(err.value)) +class Notification(GraphQLUnion): + __types__ = [Message, User] -def test_subscription_type_raises_error_when_defined_with_invalid_schema_str( - data_regression, -): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - __schema__ = "typo Subscription" +@pytest.mark.asyncio +async def test_basic_subscription_without_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + message_added: Message - data_regression.check(str(err.value)) + @GraphQLSubscription.source("message_added") + async def message_added_generator(obj, info): + while True: + yield {"id": "some_id", "content": "message", "author": "Anon"} + @GraphQLSubscription.resolver("message_added", graphql_type=Message) + async def resolve_message_added(message, info): + return message -def test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - __schema__ = "scalar Subscription" + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" - data_regression.check(str(err.value)) + schema = make_executable_schema(QueryType, SubscriptionType) + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } -def test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - __schema__ = "type Other" + type Subscription { + messageAdded: Message! + } - data_regression.check(str(err.value)) + type Message { + id: ID! + content: String! + author: String! + } + """, + ) + query = parse("subscription { messageAdded {id content author} }") + sub = await subscribe(schema, query) + + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") + + # Fetch the first result + result = await sub.__anext__() + + # Validate the result + assert not result.errors + assert result.data == { + "messageAdded": {"id": "some_id", "content": "message", "author": "Anon"} + } + + +@pytest.mark.asyncio +async def test_subscription_with_arguments_without_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + message_added: Message + + @GraphQLSubscription.source( + "message_added", + args={"channel": GraphQLObject.argument(description="Lorem ipsum.")}, + ) + async def message_added_generator(obj, info, channel: GraphQLID): + while True: + yield { + "id": "some_id", + "content": f"message_{channel}", + "author": "Anon", + } + + @GraphQLSubscription.resolver( + "message_added", + graphql_type=Message, + ) + async def resolve_message_added(message, *_, channel: GraphQLID): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } -def test_subscription_type_raises_error_when_defined_without_fields(data_regression): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class UsersSubscription(SubscriptionType): - __schema__ = "type Subscription" + type Subscription { + messageAdded( + \"\"\"Lorem ipsum.\"\"\" + channel: ID! + ): Message! + } - data_regression.check(str(err.value)) + type Message { + id: ID! + content: String! + author: String! + } + """, + ) + query = parse('subscription { messageAdded(channel: "123") {id content author} }') + sub = await subscribe(schema, query) + + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") + + # Fetch the first result + result = await sub.__anext__() + + # Validate the result + assert not result.errors + assert result.data == { + "messageAdded": {"id": "some_id", "content": "message_123", "author": "Anon"} + } + + +@pytest.mark.asyncio +async def test_multiple_supscriptions_without_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + message_added: Message + user_joined: User + + @GraphQLSubscription.source( + "message_added", + args={"channel": GraphQLObject.argument(description="Lorem ipsum.")}, + ) + async def message_added_generator(obj, info, channel: GraphQLID): + while True: + yield { + "id": "some_id", + "content": f"message_{channel}", + "author": "Anon", + } + + @GraphQLSubscription.resolver( + "message_added", + graphql_type=Message, + ) + async def resolve_message_added(message, *_, channel: GraphQLID): + return message + + @GraphQLSubscription.source( + "user_joined", + ) + async def user_joined_generator(obj, info): + while True: + yield { + "id": "some_id", + "username": "username", + } + + @GraphQLSubscription.resolver( + "user_joined", + graphql_type=Message, + ) + async def resolve_user_joined(user, *_): + return user + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } -def test_subscription_type_extracts_graphql_name(): - class UsersSubscription(SubscriptionType): - __schema__ = """ type Subscription { - thread: ID! + messageAdded( + \"\"\"Lorem ipsum.\"\"\" + channel: ID! + ): Message! + userJoined: User! + } + + type Message { + id: ID! + content: String! + author: String! } + + type User { + id: ID! + username: String! + } + """, + ) + + query = parse("subscription { userJoined {id username} }") + sub = await subscribe(schema, query) + + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") + + # Fetch the first result + result = await sub.__anext__() + + # Validate the result + assert not result.errors + assert result.data == {"userJoined": {"id": "some_id", "username": "username"}} + + +@pytest.mark.asyncio +async def test_subscription_with_complex_data_without_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + messages_in_channel: List[Message] + + @GraphQLSubscription.source( + "messages_in_channel", + args={"channel_id": GraphQLObject.argument(description="Lorem ipsum.")}, + ) + async def message_added_generator(obj, info, channel_id: GraphQLID): + while True: + yield [ + { + "id": "some_id", + "content": f"message_{channel_id}", + "author": "Anon", + } + ] + + @GraphQLSubscription.resolver( + "messages_in_channel", + graphql_type=Message, + ) + async def resolve_message_added(message, *_, channel_id: GraphQLID): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType) + + assert_schema_equals( + schema, """ + type Query { + searchSth: String! + } - assert UsersSubscription.graphql_name == "Subscription" + type Subscription { + messagesInChannel( + \"\"\"Lorem ipsum.\"\"\" + channelId: ID! + ): [Message!]! + } + type Message { + id: ID! + content: String! + author: String! + } + """, + ) -def test_subscription_type_raises_error_when_defined_without_return_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ - type Subscription { - chat: Chat - Chats: [Chat!] - } - """ + query = parse( + 'subscription { messagesInChannel(channelId: "123") {id content author} }' + ) + sub = await subscribe(schema, query) + + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") + + # Fetch the first result + result = await sub.__anext__() + + # Validate the result + assert not result.errors + assert result.data == { + "messagesInChannel": [ + {"id": "some_id", "content": "message_123", "author": "Anon"} + ] + } + + +@pytest.mark.asyncio +async def test_subscription_with_union_without_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + notification_received: Notification + + @GraphQLSubscription.source( + "notification_received", + ) + async def message_added_generator(obj, info): + while True: + yield Message(id=1, content="content", author="anon") + + @GraphQLSubscription.resolver( + "notification_received", + ) + async def resolve_message_added(message, *_): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } - data_regression.check(str(err.value)) + type Subscription { + notificationReceived: Notification! + } + union Notification = Message | User -def test_subscription_type_verifies_field_dependency(): - # pylint: disable=unused-variable - class ChatType(ObjectType): - __schema__ = """ - type Chat { - id: ID! + type Message { + id: ID! + content: String! + author: String! } - """ - class ChatSubscription(SubscriptionType): - __schema__ = """ - type Subscription { - chat: Chat - Chats: [Chat!] + type User { + id: ID! + username: String! } - """ - __requires__ = [ChatType] + """, + ) + + query = parse("subscription { notificationReceived { ... on Message { id } } }") + sub = await subscribe(schema, query) + + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") + + # Fetch the first result + result = await sub.__anext__() + # Validate the result + assert not result.errors + assert result.data == {"notificationReceived": {"id": "1"}} -def test_subscription_type_raises_error_when_defined_without_argument_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ + +@pytest.mark.asyncio +async def test_basic_subscription_with_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + __schema__ = gql( + """ type Subscription { - chat(input: ChannelInput): [String!]! + messageAdded: Message! } """ + ) + + @GraphQLSubscription.source("messageAdded") + async def message_added_generator(obj, info): + while True: + yield {"id": "some_id", "content": "message", "author": "Anon"} + + @GraphQLSubscription.resolver("messageAdded", graphql_type=Message) + async def resolve_message_added(message, info): + return message - data_regression.check(str(err.value)) + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + schema = make_executable_schema(QueryType, SubscriptionType, Message) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } -def test_subscription_type_can_be_extended_with_new_fields(): - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ type Subscription { - chat: ID! + messageAdded: Message! } - """ - class ExtendChatSubscription(SubscriptionType): - __schema__ = """ - extend type Subscription { - thread: ID! + type Message { + id: ID! + content: String! + author: String! } - """ - __requires__ = [ChatSubscription] + """, + ) + query = parse("subscription { messageAdded {id content author} }") + sub = await subscribe(schema, query) -def test_subscription_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on OBJECT" - __visitor__ = SchemaDirectiveVisitor + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") - class ChatSubscription(SubscriptionType): - __schema__ = """ - type Subscription { - chat: ID! - } - """ + # Fetch the first result + result = await sub.__anext__() - class ExtendChatSubscription(SubscriptionType): - __schema__ = "extend type Subscription @example" - __requires__ = [ChatSubscription, ExampleDirective] + # Validate the result + assert not result.errors + assert result.data == { + "messageAdded": {"id": "some_id", "content": "message", "author": "Anon"} + } -def test_subscription_type_can_be_extended_with_interface(): - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Interface { - threads: ID! - } +@pytest.mark.asyncio +async def test_subscription_with_arguments_with_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + __schema__ = gql( + """ + type Subscription { + messageAdded(channel: ID!): Message! + } + """ + ) + + @GraphQLSubscription.source( + "messageAdded", + ) + async def message_added_generator(obj, info, channel: GraphQLID): + while True: + yield { + "id": "some_id", + "content": f"message_{channel}", + "author": "Anon", + } + + @GraphQLSubscription.resolver( + "messageAdded", + graphql_type=Message, + ) + async def resolve_message_added(message, *_, channel: GraphQLID): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType, Message) + + assert_schema_equals( + schema, """ + type Query { + searchSth: String! + } - class ChatSubscription(SubscriptionType): - __schema__ = """ type Subscription { - chat: ID! + messageAdded(channel: ID!): Message! } - """ - class ExtendChatSubscription(SubscriptionType): - __schema__ = """ - extend type Subscription implements Interface { - threads: ID! + type Message { + id: ID! + content: String! + author: String! } - """ - __requires__ = [ChatSubscription, ExampleInterface] - - -def test_subscription_type_raises_error_when_defined_without_extended_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExtendChatSubscription(SubscriptionType): - __schema__ = """ - extend type Subscription { - thread: ID! - } - """ + """, + ) - data_regression.check(str(err.value)) + query = parse('subscription { messageAdded(channel: "123") {id content author} }') + sub = await subscribe(schema, query) + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") -def test_subscription_type_raises_error_when_extended_dependency_is_wrong_type( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleInterface(InterfaceType): - __schema__ = """ - interface Subscription { - id: ID! - } - """ + # Fetch the first result + result = await sub.__anext__() - class ExtendChatSubscription(SubscriptionType): - __schema__ = """ - extend type Subscription { - thread: ID! - } - """ - __requires__ = [ExampleInterface] - - data_regression.check(str(err.value)) + # Validate the result + assert not result.errors + assert result.data == { + "messageAdded": {"id": "some_id", "content": "message_123", "author": "Anon"} + } -def test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ +@pytest.mark.asyncio +async def test_multiple_supscriptions_with_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + __schema__ = gql( + """ type Subscription { - chat: ID! + messageAdded: Message! + userJoined: User! } """ - __aliases__ = { - "userAlerts": "user_alerts", - } + ) + + @GraphQLSubscription.source( + "messageAdded", + ) + async def message_added_generator(obj, info): + while True: + yield { + "id": "some_id", + "content": "message", + "author": "Anon", + } + + @GraphQLSubscription.resolver( + "messageAdded", + ) + async def resolve_message_added(message, *_): + return message + + @GraphQLSubscription.source( + "userJoined", + ) + async def user_joined_generator(obj, info): + while True: + yield { + "id": "some_id", + "username": "username", + } + + @GraphQLSubscription.resolver( + "userJoined", + ) + async def resolve_user_joined(user, *_): + return user + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType, Message, User) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } - data_regression.check(str(err.value)) + type Subscription { + messageAdded: Message! + userJoined: User! + } + type Message { + id: ID! + content: String! + author: String! + } -def test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ - type Subscription { - chat: ID! - } - """ + type User { + id: ID! + username: String! + } + """, + ) + + query = parse("subscription { userJoined {id username} }") + sub = await subscribe(schema, query) - @staticmethod - def resolve_group(*_): - return None + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") - data_regression.check(str(err.value)) + # Fetch the first result + result = await sub.__anext__() + # Validate the result + assert not result.errors + assert result.data == {"userJoined": {"id": "some_id", "username": "username"}} -def test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ChatSubscription(SubscriptionType): - __schema__ = """ + +@pytest.mark.asyncio +async def test_subscription_with_complex_data_with_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + __schema__ = gql( + """ type Subscription { - chat: ID! + messagesInChannel(channelId: ID!): [Message!]! } """ + ) + + @GraphQLSubscription.source( + "messagesInChannel", + ) + async def message_added_generator(obj, info, channelId: GraphQLID): + while True: + yield [ + { + "id": "some_id", + "content": f"message_{channelId}", + "author": "Anon", + } + ] + + @GraphQLSubscription.resolver( + "messagesInChannel", + ) + async def resolve_message_added(message, *_, channelId: GraphQLID): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType, Message) + + assert_schema_equals( + schema, + """ + type Query { + searchSth: String! + } + + type Subscription { + messagesInChannel(channelId: ID!): [Message!]! + } - @staticmethod - def subscribe_group(*_): - return None + type Message { + id: ID! + content: String! + author: String! + } + """, + ) - data_regression.check(str(err.value)) + query = parse( + 'subscription { messagesInChannel(channelId: "123") {id content author} }' + ) + sub = await subscribe(schema, query) + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") -def test_subscription_type_binds_resolver_and_subscriber_to_schema(): - schema = build_schema( - """ - type Query { - hello: String - } + # Fetch the first result + result = await sub.__anext__() + # Validate the result + assert not result.errors + assert result.data == { + "messagesInChannel": [ + {"id": "some_id", "content": "message_123", "author": "Anon"} + ] + } + + +@pytest.mark.asyncio +async def test_subscription_with_union_with_schema(assert_schema_equals): + class SubscriptionType(GraphQLSubscription): + __schema__ = gql( + """ type Subscription { - chat: ID! + notificationReceived: Notification! } + """ + ) + + @GraphQLSubscription.source( + "notificationReceived", + ) + async def message_added_generator(obj, info): + while True: + yield Message(id=1, content="content", author="anon") + + @GraphQLSubscription.resolver( + "notificationReceived", + ) + async def resolve_message_added(message, *_): + return message + + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=str) + def search_sth(*_) -> str: + return "search" + + schema = make_executable_schema(QueryType, SubscriptionType, Notification) + + assert_schema_equals( + schema, """ - ) + type Query { + searchSth: String! + } - class ChatSubscription(SubscriptionType): - __schema__ = """ type Subscription { - chat: ID! + notificationReceived: Notification! } - """ - @staticmethod - def resolve_chat(*_): - return None + union Notification = Message | User + + type Message { + id: ID! + content: String! + author: String! + } + + type User { + id: ID! + username: String! + } + """, + ) + + query = parse("subscription { notificationReceived { ... on Message { id } } }") + sub = await subscribe(schema, query) - @staticmethod - def subscribe_chat(*_): - return None + # Ensure the subscription is an async iterator + assert hasattr(sub, "__aiter__") - ChatSubscription.__bind_to_schema__(schema) + # Fetch the first result + result = await sub.__anext__() - field = schema.type_map.get("Subscription").fields["chat"] - assert field.resolve is ChatSubscription.resolve_chat - assert field.subscribe is ChatSubscription.subscribe_chat + # Validate the result + assert not result.errors + assert result.data == {"notificationReceived": {"id": "1"}} diff --git a/tests_next/test_subscription_type_validation.py b/tests/test_subscription_type_validation.py similarity index 99% rename from tests_next/test_subscription_type_validation.py rename to tests/test_subscription_type_validation.py index 4b59cc7..2f5d1df 100644 --- a/tests_next/test_subscription_type_validation.py +++ b/tests/test_subscription_type_validation.py @@ -1,6 +1,6 @@ from ariadne import gql import pytest -from ariadne_graphql_modules.next import ( +from ariadne_graphql_modules import ( GraphQLID, GraphQLObject, GraphQLSubscription, diff --git a/tests_next/test_typing.py b/tests/test_typing.py similarity index 95% rename from tests_next/test_typing.py rename to tests/test_typing.py index 9e4e3fb..3723470 100644 --- a/tests_next/test_typing.py +++ b/tests/test_typing.py @@ -5,8 +5,8 @@ from graphql import ListTypeNode, NameNode, NamedTypeNode, NonNullTypeNode import pytest -from ariadne_graphql_modules.next import GraphQLObject, deferred, graphql_enum -from ariadne_graphql_modules.next.typing import get_graphql_type, get_type_node +from ariadne_graphql_modules import GraphQLObject, deferred, graphql_enum +from ariadne_graphql_modules.typing import get_graphql_type, get_type_node if TYPE_CHECKING: from .types import ForwardEnum, ForwardScalar @@ -124,7 +124,7 @@ def test_get_non_null_graphql_list_type_node_from_python_builtin_type(metadata): def test_get_graphql_type_node_from_annotated_type(metadata): class MockType(GraphQLObject): - field: Annotated["ForwardScalar", deferred("tests_next.types")] + field: Annotated["ForwardScalar", deferred("tests.types")] assert_non_null_type( get_type_node(metadata, MockType.__annotations__["field"]), "Forward" @@ -142,7 +142,7 @@ class MockType(GraphQLObject): def test_get_graphql_type_node_from_nullable_annotated_type(metadata): class MockType(GraphQLObject): - field: Optional[Annotated["ForwardScalar", deferred("tests_next.types")]] + field: Optional[Annotated["ForwardScalar", deferred("tests.types")]] assert_named_type( get_type_node(metadata, MockType.__annotations__["field"]), "Forward" @@ -151,7 +151,7 @@ class MockType(GraphQLObject): def test_get_graphql_type_node_from_annotated_enum(metadata): class MockType(GraphQLObject): - field: Annotated["ForwardEnum", deferred("tests_next.types")] + field: Annotated["ForwardEnum", deferred("tests.types")] assert_non_null_type( get_type_node(metadata, MockType.__annotations__["field"]), "ForwardEnum" diff --git a/tests/test_union_type.py b/tests/test_union_type.py index 61c8a3a..da926c5 100644 --- a/tests/test_union_type.py +++ b/tests/test_union_type.py @@ -1,251 +1,223 @@ -from dataclasses import dataclass +from typing import List, Union -import pytest -from ariadne import SchemaDirectiveVisitor -from graphql import GraphQLError, graphql_sync +from graphql import graphql_sync from ariadne_graphql_modules import ( - DirectiveType, - ObjectType, - UnionType, + GraphQLID, + GraphQLObject, + GraphQLUnion, make_executable_schema, ) -def test_union_type_raises_attribute_error_when_defined_without_schema(data_regression): - with pytest.raises(AttributeError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - pass +class UserType(GraphQLObject): + id: GraphQLID + username: str - data_regression.check(str(err.value)) +class CommentType(GraphQLObject): + id: GraphQLID + content: str -def test_union_type_raises_error_when_defined_with_invalid_schema_type(data_regression): - with pytest.raises(TypeError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = True - data_regression.check(str(err.value)) +def test_union_field_returning_object_instance(assert_schema_equals): + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(id=1, username="Bob"), + CommentType(id=2, content="Hello World!"), + ] -def test_union_type_raises_error_when_defined_with_invalid_schema_str(data_regression): - with pytest.raises(GraphQLError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = "unien Example = A | B" - - data_regression.check(str(err.value)) - - -def test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = "scalar DateTime" - - data_regression.check(str(err.value)) - - -def test_union_type_raises_error_when_defined_with_multiple_types_schema( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = """ - union A = C | D - - union B = C | D - """ - - data_regression.check(str(err.value)) - - -@dataclass -class User: - id: int - name: str + schema = make_executable_schema(QueryType) + assert_schema_equals( + schema, + """ + type Query { + search: [Result!]! + } -@dataclass -class Comment: - id: int - message: str + union Result = User | Comment + type User { + id: ID! + username: String! + } -class UserType(ObjectType): - __schema__ = """ - type User { - id: ID! - name: String! - } - """ + type Comment { + id: ID! + content: String! + } + """, + ) + result = graphql_sync( + schema, + """ + { + search { + ... on User { + id + username + } + ... on Comment { + id + content + } + } + } + """, + ) -class CommentType(ObjectType): - __schema__ = """ - type Comment { - id: ID! - message: String! + assert not result.errors + assert result.data == { + "search": [ + {"id": "1", "username": "Bob"}, + {"id": "2", "content": "Hello World!"}, + ] } - """ - -class ResultUnion(UnionType): - __schema__ = "union Result = Comment | User" - __requires__ = [CommentType, UserType] - @staticmethod - def resolve_type(instance, *_): - if isinstance(instance, Comment): - return "Comment" +def test_union_field_returning_empty_list(): + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] - if isinstance(instance, User): - return "User" + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [] - return None + schema = make_executable_schema(QueryType) + result = graphql_sync( + schema, + """ + { + search { + ... on User { + id + username + } + ... on Comment { + id + content + } + } + } + """, + ) + assert not result.errors + assert result.data == {"search": []} -class QueryType(ObjectType): - __schema__ = """ - type Query { - results: [Result!]! - } - """ - __requires__ = [ResultUnion] - - @staticmethod - def resolve_results(*_): - return [ - User(id=1, name="Alice"), - Comment(id=1, message="Hello world!"), - ] +def test_union_field_with_invalid_type_access(assert_schema_equals): + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] -schema = make_executable_schema(QueryType, UserType, CommentType) + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(id=1, username="Bob"), + "InvalidType", + ] + schema = make_executable_schema(QueryType) -def test_union_type_extracts_graphql_name(): - class ExampleUnion(UnionType): - __schema__ = "union Example = User | Comment" - __requires__ = [UserType, CommentType] + result = graphql_sync( + schema, + """ + { + search { + ... on User { + id + username + } + ... on Comment { + id + content + } + } + } + """, + ) + assert result.errors + assert "InvalidType" in str(result.errors) - assert ExampleUnion.graphql_name == "Example" +def test_serialization_error_handling(assert_schema_equals): + class InvalidType: + def __init__(self, value): + self.value = value -def test_union_type_raises_error_when_defined_without_member_type_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = "union Example = User | Comment" - __requires__ = [UserType] + class ResultType(GraphQLUnion): + __types__ = [UserType, CommentType] - data_regression.check(str(err.value)) + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[ResultType]) + def search(*_) -> List[Union[UserType, CommentType, InvalidType]]: + return [InvalidType("This should cause an error")] + schema = make_executable_schema(QueryType) -def test_interface_type_binds_type_resolver(): - query = """ - query { - results { - ... on User { - __typename - id - name - } - ... on Comment { - __typename - id - message + result = graphql_sync( + schema, + """ + { + search { + ... on User { + id + username + } } } - } - """ - - result = graphql_sync(schema, query) - assert result.data == { - "results": [ - { - "__typename": "User", - "id": "1", - "name": "Alice", - }, - { - "__typename": "Comment", - "id": "1", - "message": "Hello world!", - }, - ], - } + """, + ) + assert result.errors -def test_union_type_can_be_extended_with_new_types(): - # pylint: disable=unused-variable - class ExampleUnion(UnionType): - __schema__ = "union Result = User | Comment" - __requires__ = [UserType, CommentType] - - class ThreadType(ObjectType): +def test_union_with_schema_definition(assert_schema_equals): + class SearchResultUnion(GraphQLUnion): __schema__ = """ - type Thread { - id: ID! - title: String! - } + union SearchResult = User | Comment """ + __types__ = [UserType, CommentType] - class ExtendExampleUnion(UnionType): - __schema__ = "union Result = Thread" - __requires__ = [ExampleUnion, ThreadType] - + class QueryType(GraphQLObject): + @GraphQLObject.field(graphql_type=List[SearchResultUnion]) + def search(*_) -> List[Union[UserType, CommentType]]: + return [ + UserType(id="1", username="Alice"), + CommentType(id="2", content="Test post"), + ] -def test_union_type_can_be_extended_with_directive(): - # pylint: disable=unused-variable - class ExampleDirective(DirectiveType): - __schema__ = "directive @example on UNION" - __visitor__ = SchemaDirectiveVisitor + schema = make_executable_schema(QueryType, SearchResultUnion) - class ExampleUnion(UnionType): - __schema__ = "union Result = User | Comment" - __requires__ = [UserType, CommentType] - - class ExtendExampleUnion(UnionType): - __schema__ = """ - extend union Result @example + result = graphql_sync( + schema, """ - __requires__ = [ExampleUnion, ExampleDirective] - - -def test_union_type_raises_error_when_defined_without_extended_dependency( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExtendExampleUnion(UnionType): - __schema__ = "extend union Result = User" - __requires__ = [UserType] - - data_regression.check(str(err.value)) - - -def test_union_type_raises_error_when_extended_dependency_is_wrong_type( - data_regression, -): - with pytest.raises(ValueError) as err: - # pylint: disable=unused-variable - class ExampleType(ObjectType): - __schema__ = """ - type Example { - id: ID! + { + search { + ... on User { + id + username + } + ... on Comment { + id + content + } } - """ - - class ExtendExampleUnion(UnionType): - __schema__ = "extend union Example = User" - __requires__ = [ExampleType, UserType] - - data_regression.check(str(err.value)) + } + """, + ) + assert not result.errors + assert result.data == { + "search": [ + {"id": "1", "username": "Alice"}, + {"id": "2", "content": "Test post"}, + ] + } diff --git a/tests_next/test_union_type_validation.py b/tests/test_union_type_validation.py similarity index 92% rename from tests_next/test_union_type_validation.py rename to tests/test_union_type_validation.py index f9930d4..fac5b1f 100644 --- a/tests_next/test_union_type_validation.py +++ b/tests/test_union_type_validation.py @@ -1,9 +1,9 @@ -from ariadne_graphql_modules.next import ( +from ariadne_graphql_modules import ( GraphQLID, GraphQLObject, GraphQLUnion, ) -from ariadne_graphql_modules.next.graphql_union.validators import ( +from ariadne_graphql_modules.union_type.validators import ( validate_union_type_with_schema, ) import pytest diff --git a/tests_next/test_validators.py b/tests/test_validators.py similarity index 92% rename from tests_next/test_validators.py rename to tests/test_validators.py index 45f4948..bdb50e2 100644 --- a/tests_next/test_validators.py +++ b/tests/test_validators.py @@ -1,8 +1,7 @@ import pytest from graphql import parse -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next.validators import validate_description, validate_name +from ariadne_graphql_modules.validators import validate_description, validate_name def test_description_validator_passes_type_without_description(): diff --git a/tests_next/test_value_node.py b/tests/test_value_node.py similarity index 96% rename from tests_next/test_value_node.py rename to tests/test_value_node.py index 48c754b..e557438 100644 --- a/tests_next/test_value_node.py +++ b/tests/test_value_node.py @@ -14,7 +14,7 @@ print_ast, ) -from ariadne_graphql_modules.next import get_value_from_node, get_value_node +from ariadne_graphql_modules import get_value_from_node, get_value_node def test_get_false_value(): @@ -121,7 +121,7 @@ class CustomType: get_value_node(CustomType()) error_message = str(exc_info.value) - assert error_message.startswith("Python value '' can't be represented as a GraphQL value node.") diff --git a/tests_next/types.py b/tests/types.py similarity index 73% rename from tests_next/types.py rename to tests/types.py index 02761c7..96233ee 100644 --- a/tests_next/types.py +++ b/tests/types.py @@ -1,6 +1,6 @@ from enum import Enum -from ariadne_graphql_modules.next import GraphQLScalar +from ariadne_graphql_modules import GraphQLScalar class ForwardScalar(GraphQLScalar): diff --git a/tests_next/conftest.py b/tests_next/conftest.py deleted file mode 100644 index 46da232..0000000 --- a/tests_next/conftest.py +++ /dev/null @@ -1,40 +0,0 @@ -from pathlib import Path -from textwrap import dedent - -import pytest -from graphql import TypeDefinitionNode, GraphQLSchema, print_ast, print_schema - -from ariadne_graphql_modules.next import GraphQLMetadata - - -@pytest.fixture -def assert_schema_equals(): - def schema_equals_assertion(schema: GraphQLSchema, target: str): - schema_str = print_schema(schema) - assert schema_str == dedent(target).strip() - - return schema_equals_assertion - - -@pytest.fixture -def assert_ast_equals(): - def ast_equals_assertion(ast: TypeDefinitionNode, target: str): - ast_str = print_ast(ast) - assert ast_str == dedent(target).strip() - - return ast_equals_assertion - - -@pytest.fixture -def metadata(): - return GraphQLMetadata() - - -@pytest.fixture(scope="session") -def datadir() -> Path: - return Path(__file__).parent / "snapshots" - - -@pytest.fixture(scope="session") -def original_datadir() -> Path: - return Path(__file__).parent / "snapshots" diff --git a/tests_next/snapshots/test_interface_with_different_types.obtained.yml b/tests_next/snapshots/test_interface_with_different_types.obtained.yml deleted file mode 100644 index f10dac9..0000000 --- a/tests_next/snapshots/test_interface_with_different_types.obtained.yml +++ /dev/null @@ -1,5 +0,0 @@ -'Query root type must be provided. - - - Interface field UserInterface.score expects type String! but User.score is type - Int!.' diff --git a/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml b/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml deleted file mode 100644 index d7bf90b..0000000 --- a/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.obtained.yml +++ /dev/null @@ -1 +0,0 @@ -'"No data is set for ''''."' diff --git a/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.yml b/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.yml deleted file mode 100644 index d7bf90b..0000000 --- a/tests_next/snapshots/test_metadata_raises_key_error_for_unset_data.yml +++ /dev/null @@ -1 +0,0 @@ -'"No data is set for ''''."' diff --git a/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml b/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml deleted file mode 100644 index 4eccfe9..0000000 --- a/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.obtained.yml +++ /dev/null @@ -1,3 +0,0 @@ -Types 'SecondRoot' and '.FirstRoot'>' - both define GraphQL type with name 'Query'. -... diff --git a/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml b/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml deleted file mode 100644 index 4eccfe9..0000000 --- a/tests_next/snapshots/test_multiple_roots_fail_validation_if_merge_roots_is_disabled.yml +++ /dev/null @@ -1,3 +0,0 @@ -Types 'SecondRoot' and '.FirstRoot'>' - both define GraphQL type with name 'Query'. -... diff --git a/tests_next/test_enum_type.py b/tests_next/test_enum_type.py deleted file mode 100644 index e4bf3c0..0000000 --- a/tests_next/test_enum_type.py +++ /dev/null @@ -1,522 +0,0 @@ -from enum import Enum - -from graphql import graphql_sync - -from ariadne_graphql_modules.next import ( - GraphQLEnum, - GraphQLObject, - make_executable_schema, -) - - -class UserLevelEnum(Enum): - GUEST = 0 - MEMBER = 1 - ADMIN = 2 - - -def test_enum_field_returning_enum_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __members__ = UserLevelEnum - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> UserLevelEnum: - return UserLevelEnum.MEMBER - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "MEMBER"} - - -def test_enum_field_returning_dict_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> dict: - return 0 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "GUEST"} - - -def test_enum_field_returning_str_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __members__ = [ - "GUEST", - "MEMBER", - "ADMIN", - ] - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> str: - return "ADMIN" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} - - -def test_enum_type_with_custom_name(assert_schema_equals): - class UserLevel(GraphQLEnum): - __graphql_name__ = "UserLevelEnum" - __members__ = UserLevelEnum - - class QueryType(GraphQLObject): - level: UserLevel - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevelEnum! - } - - enum UserLevelEnum { - GUEST - MEMBER - ADMIN - } - """, - ) - - -def test_enum_type_with_description(assert_schema_equals): - class UserLevel(GraphQLEnum): - __description__ = "Hello world." - __members__ = UserLevelEnum - - class QueryType(GraphQLObject): - level: UserLevel - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - \"\"\"Hello world.\"\"\" - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - -def test_enum_type_with_member_description(assert_schema_equals): - class UserLevel(GraphQLEnum): - __members__ = UserLevelEnum - __members_descriptions__ = {"MEMBER": "Hello world."} - - class QueryType(GraphQLObject): - level: UserLevel - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - - \"\"\"Hello world.\"\"\" - MEMBER - ADMIN - } - """, - ) - - -def test_schema_enum_field_returning_enum_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - __members__ = UserLevelEnum - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> UserLevelEnum: - return UserLevelEnum.MEMBER - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "MEMBER"} - - -def test_schema_enum_field_returning_dict_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> int: - return 2 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} - - -def test_schema_enum_field_returning_str_value(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> str: - return "GUEST" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "GUEST"} - - -def test_schema_enum_with_description_attr(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - __description__ = "Hello world." - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> int: - return 2 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - \"\"\"Hello world.\"\"\" - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} - - -def test_schema_enum_with_schema_description(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - \"\"\"Hello world.\"\"\" - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> int: - return 2 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - \"\"\"Hello world.\"\"\" - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} - - -def test_schema_enum_with_member_description(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - MEMBER - ADMIN - } - """ - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - __members_descriptions__ = {"MEMBER": "Hello world."} - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> int: - return 2 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - - \"\"\"Hello world.\"\"\" - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} - - -def test_schema_enum_with_member_schema_description(assert_schema_equals): - class UserLevel(GraphQLEnum): - __schema__ = """ - enum UserLevel { - GUEST - \"\"\"Hello world.\"\"\" - MEMBER - ADMIN - } - """ - __members__ = { - "GUEST": 0, - "MEMBER": 1, - "ADMIN": 2, - } - - class QueryType(GraphQLObject): - level: UserLevel - - @GraphQLObject.resolver("level") - def resolve_level(*_) -> int: - return 2 - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - level: UserLevel! - } - - enum UserLevel { - GUEST - - \"\"\"Hello world.\"\"\" - MEMBER - ADMIN - } - """, - ) - - result = graphql_sync(schema, "{ level }") - - assert not result.errors - assert result.data == {"level": "ADMIN"} diff --git a/tests_next/test_input_type.py b/tests_next/test_input_type.py deleted file mode 100644 index 977dfcb..0000000 --- a/tests_next/test_input_type.py +++ /dev/null @@ -1,628 +0,0 @@ -from typing import Optional - -import pytest -from graphql import graphql_sync - -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import ( - GraphQLInput, - GraphQLObject, - make_executable_schema, -) - - -def test_input_type_instance_with_all_attrs_values(): - class SearchInput(GraphQLInput): - query: str - age: int - - obj = SearchInput(query="search", age=20) - assert obj.query == "search" - assert obj.age == 20 - - -def test_input_type_instance_with_omitted_attrs_being_none(): - class SearchInput(GraphQLInput): - query: str - age: int - - obj = SearchInput(age=20) - assert obj.query is None - assert obj.age == 20 - - -def test_input_type_instance_with_default_attrs_values(): - class SearchInput(GraphQLInput): - query: str = "default" - age: int = 42 - - obj = SearchInput(age=20) - assert obj.query == "default" - assert obj.age == 20 - - -def test_input_type_instance_with_all_fields_values(): - class SearchInput(GraphQLInput): - query: str = GraphQLInput.field() - age: int = GraphQLInput.field() - - obj = SearchInput(query="search", age=20) - assert obj.query == "search" - assert obj.age == 20 - - -def test_input_type_instance_with_all_fields_default_values(): - class SearchInput(GraphQLInput): - query: str = GraphQLInput.field(default_value="default") - age: int = GraphQLInput.field(default_value=42) - - obj = SearchInput(age=20) - assert obj.query == "default" - assert obj.age == 20 - - -def test_input_type_instance_with_invalid_attrs_raising_error(data_regression): - class SearchInput(GraphQLInput): - query: str - age: int - - with pytest.raises(TypeError) as exc_info: - SearchInput(age=20, invalid="Ok") - - data_regression.check(str(exc_info.value)) - - -def test_schema_input_type_instance_with_all_attrs_values(): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String - age: Int - } - """ - ) - - query: str - age: int - - obj = SearchInput(query="search", age=20) - assert obj.query == "search" - assert obj.age == 20 - - -def test_schema_input_type_instance_with_omitted_attrs_being_none(): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String - age: Int - } - """ - ) - - query: str - age: int - - obj = SearchInput(age=20) - assert obj.query is None - assert obj.age == 20 - - -def test_schema_input_type_instance_with_default_attrs_values(): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String = "default" - age: Int = 42 - } - """ - ) - - query: str - age: int - - obj = SearchInput(age=20) - assert obj.query == "default" - assert obj.age == 20 - - -def test_schema_input_type_instance_with_all_attrs_default_values(): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String = "default" - age: Int = 42 - } - """ - ) - - query: str - age: int - - obj = SearchInput() - assert obj.query == "default" - assert obj.age == 42 - - -def test_schema_input_type_instance_with_default_attrs_python_values(): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String - age: Int - } - """ - ) - - query: str = "default" - age: int = 42 - - obj = SearchInput(age=20) - assert obj.query == "default" - assert obj.age == 20 - - -def test_schema_input_type_instance_with_invalid_attrs_raising_error(data_regression): - class SearchInput(GraphQLInput): - __schema__ = gql( - """ - input Search { - query: String - age: Int - } - """ - ) - - query: str - age: int - - with pytest.raises(TypeError) as exc_info: - SearchInput(age=20, invalid="Ok") - - data_regression.check(str(exc_info.value)) - - -def test_input_type_arg(assert_schema_equals): - class SearchInput(GraphQLInput): - query: Optional[str] - age: Optional[int] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - age: Int - } - """, - ) - - result = graphql_sync(schema, '{ search(input: { query: "Hello" }) }') - - assert not result.errors - assert result.data == {"search": "['Hello', None]"} - - -def test_schema_input_type_arg(assert_schema_equals): - class SearchInput(GraphQLInput): - __schema__ = """ - input SearchInput { - query: String - age: Int - } - """ - - query: Optional[str] - age: Optional[int] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - age: Int - } - """, - ) - - result = graphql_sync(schema, '{ search(input: { query: "Hello" }) }') - - assert not result.errors - assert result.data == {"search": "['Hello', None]"} - - -def test_input_type_automatic_out_name_arg(assert_schema_equals): - class SearchInput(GraphQLInput): - query: Optional[str] - min_age: Optional[int] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.min_age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - minAge: Int - } - """, - ) - - result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") - - assert not result.errors - assert result.data == {"search": "[None, 21]"} - - -def test_schema_input_type_automatic_out_name_arg(assert_schema_equals): - class SearchInput(GraphQLInput): - __schema__ = """ - input SearchInput { - query: String - minAge: Int - } - """ - - query: Optional[str] - min_age: Optional[int] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.min_age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - minAge: Int - } - """, - ) - - result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") - - assert not result.errors - assert result.data == {"search": "[None, 21]"} - - -def test_schema_input_type_explicit_out_name_arg(assert_schema_equals): - class SearchInput(GraphQLInput): - __schema__ = """ - input SearchInput { - query: String - minAge: Int - } - """ - __out_names__ = {"minAge": "age"} - - query: Optional[str] - age: Optional[int] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - minAge: Int - } - """, - ) - - result = graphql_sync(schema, "{ search(input: { minAge: 21 }) }") - - assert not result.errors - assert result.data == {"search": "[None, 21]"} - - -def test_input_type_self_reference(assert_schema_equals): - class SearchInput(GraphQLInput): - query: Optional[str] - extra: Optional["SearchInput"] - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - if input.extra: - extra_repr = input.extra.query - else: - extra_repr = None - - return f"{repr([input.query, extra_repr])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String - extra: SearchInput - } - """, - ) - - result = graphql_sync( - schema, - """ - { - search( - input: { query: "Hello", extra: { query: "Other" } } - ) - } - """, - ) - - assert not result.errors - assert result.data == {"search": "['Hello', 'Other']"} - - -def test_schema_input_type_with_default_value(assert_schema_equals): - class SearchInput(GraphQLInput): - __schema__ = """ - input SearchInput { - query: String = "Search" - age: Int = 42 - } - """ - - query: str - age: int - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String = "Search" - age: Int = 42 - } - """, - ) - - result = graphql_sync(schema, "{ search(input: {}) }") - - assert not result.errors - assert result.data == {"search": "['Search', 42]"} - - -def test_input_type_with_field_default_value(assert_schema_equals): - class SearchInput(GraphQLInput): - query: str = "default" - age: int = 42 - flag: bool = False - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age, input.flag])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String! = "default" - age: Int! = 42 - flag: Boolean! = false - } - """, - ) - - result = graphql_sync(schema, "{ search(input: {}) }") - - assert not result.errors - assert result.data == {"search": "['default', 42, False]"} - - -def test_input_type_with_field_instance_default_value(assert_schema_equals): - class SearchInput(GraphQLInput): - query: str = GraphQLInput.field(default_value="default") - age: int = GraphQLInput.field(default_value=42) - flag: bool = GraphQLInput.field(default_value=False) - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return f"{repr([input.query, input.age, input.flag])}" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: String! = "default" - age: Int! = 42 - flag: Boolean! = false - } - """, - ) - - result = graphql_sync(schema, "{ search(input: {}) }") - - assert not result.errors - assert result.data == {"search": "['default', 42, False]"} - - -def test_input_type_with_field_type(assert_schema_equals): - class SearchInput(GraphQLInput): - query: str = GraphQLInput.field(graphql_type=int) - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return str(input) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - query: Int! - } - """, - ) - - -def test_schema_input_type_with_field_description(assert_schema_equals): - class SearchInput(GraphQLInput): - __schema__ = """ - input SearchInput { - \"\"\"Hello world.\"\"\" - query: String! - } - """ - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return str(input) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - \"\"\"Hello world.\"\"\" - query: String! - } - """, - ) - - -def test_input_type_with_field_description(assert_schema_equals): - class SearchInput(GraphQLInput): - query: str = GraphQLInput.field(description="Hello world.") - - class QueryType(GraphQLObject): - search: str - - @GraphQLObject.resolver("search") - def resolve_search(*_, input: SearchInput) -> str: - return str(input) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search(input: SearchInput!): String! - } - - input SearchInput { - \"\"\"Hello world.\"\"\" - query: String! - } - """, - ) diff --git a/tests_next/test_interface_type.py b/tests_next/test_interface_type.py deleted file mode 100644 index 74064ad..0000000 --- a/tests_next/test_interface_type.py +++ /dev/null @@ -1,366 +0,0 @@ -from typing import List, Union - -from graphql import graphql_sync - -from ariadne_graphql_modules.next import ( - GraphQLID, - GraphQLObject, - GraphQLInterface, - GraphQLUnion, - make_executable_schema, -) - - -class CommentType(GraphQLObject): - id: GraphQLID - content: str - - -def test_interface_without_schema(assert_schema_equals): - class UserInterface(GraphQLInterface): - summary: str - score: int - - class UserType(GraphQLObject): - name: str - summary: str - score: int - - __implements__ = [UserInterface] - - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(id=1, username="Bob"), - CommentType(id=2, content="Hello World!"), - ] - - schema = make_executable_schema(QueryType, UserInterface, UserType) - - assert_schema_equals( - schema, - """ - type Query { - search: [Result!]! - } - - union Result = User | Comment - - type User implements UserInterface { - summary: String! - score: Int! - name: String! - } - - type Comment { - id: ID! - content: String! - } - - interface UserInterface { - summary: String! - score: Int! - } - - """, - ) - - -def test_interface_inheritance_without_schema(assert_schema_equals): - def hello_resolver(*_, name: str) -> str: - return f"Hello {name}!" - - class UserInterface(GraphQLInterface): - summary: str - score: str = GraphQLInterface.field( - hello_resolver, - name="better_score", - graphql_type=str, - args={"name": GraphQLInterface.argument(name="json")}, - description="desc", - default_value="my_json", - ) - - class UserType(GraphQLObject): - name: str = GraphQLInterface.field( - name="name", - graphql_type=str, - args={"name": GraphQLInterface.argument(name="json")}, - default_value="my_json", - ) - - __implements__ = [UserInterface] - - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(), - CommentType(id=2, content="Hello World!"), - ] - - schema = make_executable_schema(QueryType, UserInterface, UserType) - - assert_schema_equals( - schema, - """ - type Query { - search: [Result!]! - } - - union Result = User | Comment - - type User implements UserInterface { - summary: String! - - \"\"\"desc\"\"\" - better_score(json: String!): String! - name: String! - } - - type Comment { - id: ID! - content: String! - } - - interface UserInterface { - summary: String! - - \"\"\"desc\"\"\" - better_score(json: String!): String! - } - - """, - ) - - result = graphql_sync( - schema, '{ search { ... on User{ better_score(json: "test") } } }' - ) - - assert not result.errors - assert result.data == {"search": [{"better_score": "Hello test!"}, {}]} - - -def test_interface_with_schema(assert_schema_equals): - class UserInterface(GraphQLInterface): - __schema__ = """ - interface UserInterface { - summary: String! - score: Int! - } - """ - - class UserType(GraphQLObject): - __schema__ = """ - type User implements UserInterface { - id: ID! - name: String! - summary: String! - score: Int! - } - """ - - __implements__ = [UserInterface] - - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(id=1, username="Bob"), - CommentType(id=2, content="Hello World!"), - ] - - schema = make_executable_schema(QueryType, UserType) - - assert_schema_equals( - schema, - """ - type Query { - search: [Result!]! - } - - union Result = User | Comment - - type User implements UserInterface { - id: ID! - name: String! - summary: String! - score: Int! - } - - interface UserInterface { - summary: String! - score: Int! - } - - type Comment { - id: ID! - content: String! - } - - """, - ) - - -def test_interface_inheritance(assert_schema_equals): - class BaseEntityInterface(GraphQLInterface): - id: GraphQLID - - class UserInterface(GraphQLInterface): - username: str - - __implements__ = [BaseEntityInterface] - - class UserType(GraphQLObject): - - __implements__ = [UserInterface, BaseEntityInterface] - - class QueryType(GraphQLObject): - @GraphQLObject.field - def user(*_) -> UserType: - return UserType(id="1", username="test_user") - - schema = make_executable_schema( - QueryType, BaseEntityInterface, UserInterface, UserType - ) - - assert_schema_equals( - schema, - """ - type Query { - user: User! - } - - type User implements UserInterface & BaseEntityInterface { - id: ID! - username: String! - } - - interface UserInterface implements BaseEntityInterface { - id: ID! - username: String! - } - - interface BaseEntityInterface { - id: ID! - } - """, - ) - - -def test_interface_descriptions(assert_schema_equals): - class UserInterface(GraphQLInterface): - summary: str - score: int - - __description__ = "Lorem ipsum." - - class UserType(GraphQLObject): - id: GraphQLID - username: str - - __implements__ = [UserInterface] - - class QueryType(GraphQLObject): - @GraphQLObject.field - def user(*_) -> UserType: - return UserType(id="1", username="test_user") - - schema = make_executable_schema(QueryType, UserType, UserInterface) - - assert_schema_equals( - schema, - """ - type Query { - user: User! - } - - type User implements UserInterface { - summary: String! - score: Int! - id: ID! - username: String! - } - - \"\"\"Lorem ipsum.\"\"\" - interface UserInterface { - summary: String! - score: Int! - } - """, - ) - - -def test_interface_resolvers_and_field_descriptions(assert_schema_equals): - class UserInterface(GraphQLInterface): - summary: str - score: int - - @GraphQLInterface.resolver("score", description="Lorem ipsum.") - def resolve_score(*_): - return 200 - - class UserType(GraphQLObject): - id: GraphQLID - - __implements__ = [UserInterface] - - class MyType(GraphQLObject): - id: GraphQLID - name: str - - __implements__ = [UserInterface] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[UserInterface]) - def users(*_) -> List[Union[UserType, MyType]]: - return [MyType(id="2", name="old", summary="ss", score=22)] - - schema = make_executable_schema(QueryType, UserType, MyType, UserInterface) - - assert_schema_equals( - schema, - """ - type Query { - users: [UserInterface!]! - } - - interface UserInterface { - summary: String! - - \"\"\"Lorem ipsum.\"\"\" - score: Int! - } - - type User implements UserInterface { - summary: String! - - \"\"\"Lorem ipsum.\"\"\" - score: Int! - id: ID! - } - - type My implements UserInterface { - summary: String! - - \"\"\"Lorem ipsum.\"\"\" - score: Int! - id: ID! - name: String! - } - """, - ) - result = graphql_sync(schema, "{ users { ... on My { __typename score } } }") - - assert not result.errors - assert result.data == {"users": [{"__typename": "My", "score": 200}]} diff --git a/tests_next/test_interface_type_validation.py b/tests_next/test_interface_type_validation.py deleted file mode 100644 index 0c96f8e..0000000 --- a/tests_next/test_interface_type_validation.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest - -from ariadne_graphql_modules.next import ( - GraphQLID, - GraphQLObject, - GraphQLInterface, - make_executable_schema, -) - - -def test_interface_with_different_types(data_regression): - with pytest.raises(TypeError) as exc_info: - - class UserInterface(GraphQLInterface): - summary: str - score: str - - class UserType(GraphQLObject): - name: str - summary: str - score: int - - __implements__ = [UserInterface] - - make_executable_schema(UserType, UserInterface) - - data_regression.check(str(exc_info.value)) - - -def test_interface_no_interface_in_schema(data_regression): - with pytest.raises(TypeError) as exc_info: - - class BaseInterface(GraphQLInterface): - id: GraphQLID - - class UserType(GraphQLObject): - id: GraphQLID - username: str - email: str - - __implements__ = [BaseInterface] - - make_executable_schema(UserType) - - data_regression.check(str(exc_info.value)) diff --git a/tests_next/test_object_type.py b/tests_next/test_object_type.py deleted file mode 100644 index 6655bec..0000000 --- a/tests_next/test_object_type.py +++ /dev/null @@ -1,1015 +0,0 @@ -from typing import Optional - -import pytest -from graphql import graphql_sync - -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import GraphQLObject, make_executable_schema - - -def test_object_type_instance_with_all_attrs_values(): - class CategoryType(GraphQLObject): - name: str - posts: int - - obj = CategoryType(name="Welcome", posts=20) - assert obj.name == "Welcome" - assert obj.posts == 20 - - -def test_object_type_instance_with_omitted_attrs_being_none(): - class CategoryType(GraphQLObject): - name: str - posts: int - - obj = CategoryType(posts=20) - assert obj.name is None - assert obj.posts == 20 - - -def test_object_type_instance_with_aliased_attrs_values(): - class CategoryType(GraphQLObject): - name: str - posts: int - - __aliases__ = {"name": "title"} - - title: str - - obj = CategoryType(title="Welcome", posts=20) - assert obj.title == "Welcome" - assert obj.posts == 20 - - -def test_object_type_instance_with_omitted_attrs_being_default_values(): - class CategoryType(GraphQLObject): - name: str = "Hello" - posts: int = 42 - - obj = CategoryType(posts=20) - assert obj.name == "Hello" - assert obj.posts == 20 - - -def test_object_type_instance_with_all_attrs_being_default_values(): - class CategoryType(GraphQLObject): - name: str = "Hello" - posts: int = 42 - - obj = CategoryType() - assert obj.name == "Hello" - assert obj.posts == 42 - - -def test_object_type_instance_with_invalid_attrs_raising_error(data_regression): - class CategoryType(GraphQLObject): - name: str - posts: int - - with pytest.raises(TypeError) as exc_info: - CategoryType(name="Welcome", invalid="Ok") - - data_regression.check(str(exc_info.value)) - - -def test_schema_object_type_instance_with_all_attrs_values(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - - name: str - posts: int - - obj = CategoryType(name="Welcome", posts=20) - assert obj.name == "Welcome" - assert obj.posts == 20 - - -def test_schema_object_type_instance_with_omitted_attrs_being_none(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - - name: str - posts: int - - obj = CategoryType(posts=20) - assert obj.name is None - assert obj.posts == 20 - - -def test_schema_object_type_instance_with_omitted_attrs_being_default_values(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - - name: str = "Hello" - posts: int = 42 - - obj = CategoryType(posts=20) - assert obj.name == "Hello" - assert obj.posts == 20 - - -def test_schema_object_type_instance_with_all_attrs_being_default_values(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - - name: str = "Hello" - posts: int = 42 - - obj = CategoryType() - assert obj.name == "Hello" - assert obj.posts == 42 - - -def test_schema_object_type_instance_with_aliased_attrs_values(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - __aliases__ = {"name": "title"} - - title: str = "Hello" - posts: int = 42 - - obj = CategoryType(title="Ok") - assert obj.title == "Ok" - assert obj.posts == 42 - - -def test_schema_object_type_instance_with_aliased_attrs_default_values(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - __aliases__ = {"name": "title"} - - title: str = "Hello" - posts: int = 42 - - obj = CategoryType() - assert obj.title == "Hello" - assert obj.posts == 42 - - -def test_schema_object_type_instance_with_invalid_attrs_raising_error(data_regression): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - - name: str - posts: int - - with pytest.raises(TypeError) as exc_info: - CategoryType(name="Welcome", invalid="Ok") - - data_regression.check(str(exc_info.value)) - - -def test_schema_object_type_instance_with_aliased_attr_value(): - class CategoryType(GraphQLObject): - __schema__ = gql( - """ - type Category { - name: String - posts: Int - } - """ - ) - __aliases__ = {"name": "title"} - - title: str - posts: int - - obj = CategoryType(title="Welcome", posts=20) - assert obj.title == "Welcome" - assert obj.posts == 20 - - -def test_object_type_with_field(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_alias(assert_schema_equals): - class QueryType(GraphQLObject): - __aliases__ = {"hello": "welcome_message"} - - hello: str - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync( - schema, "{ hello }", root_value={"welcome_message": "Hello World!"} - ) - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_alias_excludes_alias_targets(assert_schema_equals): - class QueryType(GraphQLObject): - __aliases__ = {"hello": "welcome"} - - hello: str - welcome: str - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }", root_value={"welcome": "Hello World!"}) - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_alias_includes_aliased_field_instances(assert_schema_equals): - class QueryType(GraphQLObject): - __aliases__ = {"hello": "welcome"} - - hello: str - welcome: str = GraphQLObject.field() - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - welcome: String! - } - """, - ) - - result = graphql_sync( - schema, "{ hello welcome }", root_value={"welcome": "Hello World!"} - ) - - assert not result.errors - assert result.data == {"hello": "Hello World!", "welcome": "Hello World!"} - - -def test_object_type_with_attr_automatic_alias(assert_schema_equals): - class QueryType(GraphQLObject): - test_message: str - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - testMessage: String! - } - """, - ) - - result = graphql_sync( - schema, "{ testMessage }", root_value={"test_message": "Hello World!"} - ) - - assert not result.errors - assert result.data == {"testMessage": "Hello World!"} - - -def test_object_type_with_field_instance_automatic_alias(assert_schema_equals): - class QueryType(GraphQLObject): - message: str = GraphQLObject.field(name="testMessage") - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - testMessage: String! - } - """, - ) - - result = graphql_sync( - schema, "{ testMessage }", root_value={"message": "Hello World!"} - ) - - assert not result.errors - assert result.data == {"testMessage": "Hello World!"} - - -def test_object_type_with_field_resolver(assert_schema_equals): - class QueryType(GraphQLObject): - @GraphQLObject.field - def hello(obj, info) -> str: - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_typed_field_instance(assert_schema_equals): - class QueryType(GraphQLObject): - hello = GraphQLObject.field(lambda *_: "Hello World!", graphql_type=str) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_annotated_field_instance(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str = GraphQLObject.field(lambda *_: "Hello World!") - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_typed_field_and_field_resolver(assert_schema_equals): - class QueryType(GraphQLObject): - name: str - - @GraphQLObject.field - def hello(obj, info) -> str: - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - name: String! - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ name hello }", root_value={"name": "Ok"}) - - assert not result.errors - assert result.data == {"name": "Ok", "hello": "Hello World!"} - - -def test_object_type_with_schema(assert_schema_equals): - class QueryType(GraphQLObject): - __schema__ = gql( - """ - type Query { - hello: String! - } - """ - ) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_nested_types(assert_schema_equals): - class UserType(GraphQLObject): - name: str - - class PostType(GraphQLObject): - message: str - - class QueryType(GraphQLObject): - user: UserType - - @GraphQLObject.field(graphql_type=PostType) - def post(obj, info): - return {"message": "test"} - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - user: User! - post: Post! - } - - type User { - name: String! - } - - type Post { - message: String! - } - """, - ) - - result = graphql_sync( - schema, - "{ user { name } post { message } }", - root_value={"user": {"name": "Bob"}}, - ) - - assert not result.errors - assert result.data == { - "user": { - "name": "Bob", - }, - "post": {"message": "test"}, - } - - -def test_resolver_decorator_sets_resolver_for_type_hint_field(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str - - @GraphQLObject.resolver("hello") - def resolve_hello(*_): - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_resolver_decorator_sets_resolver_for_instance_field(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str = GraphQLObject.field(name="hello") - - @GraphQLObject.resolver("hello") - def resolve_hello(*_): - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_resolver_decorator_sets_resolver_for_field_in_schema(assert_schema_equals): - class QueryType(GraphQLObject): - __schema__ = gql( - """ - type Query { - hello: String! - } - """ - ) - - @GraphQLObject.resolver("hello") - def resolve_hello(*_): - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_object_type_with_description(assert_schema_equals): - class QueryType(GraphQLObject): - __description__ = "Lorem ipsum." - - hello: str - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - \"\"\"Lorem ipsum.\"\"\" - type Query { - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }", root_value={"hello": "Hello World!"}) - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_field_decorator_sets_description_for_field(assert_schema_equals): - class QueryType(GraphQLObject): - @GraphQLObject.field(description="Lorem ipsum.") - def hello(obj, info) -> str: - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - \"\"\"Lorem ipsum.\"\"\" - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_field_decorator_sets_description_for_field_arg(assert_schema_equals): - class QueryType(GraphQLObject): - @GraphQLObject.field( - args={"name": GraphQLObject.argument(description="Lorem ipsum.")} - ) - def hello(obj, info, name: str) -> str: - return f"Hello {name}!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello( - \"\"\"Lorem ipsum.\"\"\" - name: String! - ): String! - } - """, - ) - - result = graphql_sync(schema, '{ hello(name: "Bob") }') - - assert not result.errors - assert result.data == {"hello": "Hello Bob!"} - - -def test_resolver_decorator_sets_description_for_type_hint_field(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str - - @GraphQLObject.resolver("hello", description="Lorem ipsum.") - def resolve_hello(*_): - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - \"\"\"Lorem ipsum.\"\"\" - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_resolver_decorator_sets_description_for_field_in_schema(assert_schema_equals): - class QueryType(GraphQLObject): - __schema__ = gql( - """ - type Query { - hello: String! - } - """ - ) - - @GraphQLObject.resolver("hello", description="Lorem ipsum.") - def resolve_hello(*_): - return "Hello World!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - \"\"\"Lorem ipsum.\"\"\" - hello: String! - } - """, - ) - - result = graphql_sync(schema, "{ hello }") - - assert not result.errors - assert result.data == {"hello": "Hello World!"} - - -def test_resolver_decorator_sets_description_for_field_arg(assert_schema_equals): - class QueryType(GraphQLObject): - hello: str - - @GraphQLObject.resolver( - "hello", args={"name": GraphQLObject.argument(description="Lorem ipsum.")} - ) - def resolve_hello(obj, info, name: str) -> str: - return f"Hello {name}!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello( - \"\"\"Lorem ipsum.\"\"\" - name: String! - ): String! - } - """, - ) - - result = graphql_sync(schema, '{ hello(name: "Bob") }') - - assert not result.errors - assert result.data == {"hello": "Hello Bob!"} - - -def test_schema_sets_description_for_field_arg(assert_schema_equals): - class QueryType(GraphQLObject): - __schema__ = gql( - """ - type Query { - hello( - \"\"\"Lorem ipsum.\"\"\" - name: String! - ): String! - } - """ - ) - - @GraphQLObject.resolver("hello") - def resolve_hello(*_, name: str): - return f"Hello {name}!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello( - \"\"\"Lorem ipsum.\"\"\" - name: String! - ): String! - } - """, - ) - - result = graphql_sync(schema, '{ hello(name: "Bob") }') - - assert not result.errors - assert result.data == {"hello": "Hello Bob!"} - - -def test_resolver_decorator_sets_description_for_field_arg_in_schema( - assert_schema_equals, -): - class QueryType(GraphQLObject): - __schema__ = gql( - """ - type Query { - hello(name: String!): String! - } - """ - ) - - @GraphQLObject.resolver( - "hello", args={"name": GraphQLObject.argument(description="Description")} - ) - def resolve_hello(*_, name: str): - return f"Hello {name}!" - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - hello( - \"\"\"Description\"\"\" - name: String! - ): String! - } - """, - ) - - result = graphql_sync(schema, '{ hello(name: "Bob") }') - - assert not result.errors - assert result.data == {"hello": "Hello Bob!"} - - -def test_object_type_self_reference( - assert_schema_equals, -): - class CategoryType(GraphQLObject): - name: str - parent: Optional["CategoryType"] - - class QueryType(GraphQLObject): - category: CategoryType - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - category: Category! - } - - type Category { - name: String! - parent: Category - } - """, - ) - - result = graphql_sync( - schema, - "{ category { name parent { name } } }", - root_value={ - "category": { - "name": "Lorem", - "parent": { - "name": "Ipsum", - }, - }, - }, - ) - - assert not result.errors - assert result.data == { - "category": { - "name": "Lorem", - "parent": { - "name": "Ipsum", - }, - }, - } - - -def test_object_type_return_instance( - assert_schema_equals, -): - class CategoryType(GraphQLObject): - name: str - color: str - - class QueryType(GraphQLObject): - @GraphQLObject.field() - def category(*_) -> CategoryType: - return CategoryType( - name="Welcome", - color="#FF00FF", - ) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - category: Category! - } - - type Category { - name: String! - color: String! - } - """, - ) - - result = graphql_sync(schema, "{ category { name color } }") - - assert not result.errors - assert result.data == { - "category": { - "name": "Welcome", - "color": "#FF00FF", - }, - } - - -def test_object_type_nested_type( - assert_schema_equals, -): - class UserType(GraphQLObject): - username: str - - class CategoryType(GraphQLObject): - name: str - parent: Optional["CategoryType"] - owner: UserType - - class QueryType(GraphQLObject): - category: CategoryType - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - category: Category! - } - - type Category { - name: String! - parent: Category - owner: User! - } - - type User { - username: String! - } - """, - ) - - result = graphql_sync( - schema, - "{ category { name parent { name } owner { username } } }", - root_value={ - "category": { - "name": "Lorem", - "parent": { - "name": "Ipsum", - }, - "owner": { - "username": "John", - }, - }, - }, - ) - - assert not result.errors - assert result.data == { - "category": { - "name": "Lorem", - "parent": { - "name": "Ipsum", - }, - "owner": { - "username": "John", - }, - }, - } diff --git a/tests_next/test_scalar_type.py b/tests_next/test_scalar_type.py deleted file mode 100644 index 2f578c6..0000000 --- a/tests_next/test_scalar_type.py +++ /dev/null @@ -1,111 +0,0 @@ -from datetime import date - -from graphql import graphql_sync - -from ariadne_graphql_modules import gql -from ariadne_graphql_modules.next import ( - GraphQLObject, - GraphQLScalar, - make_executable_schema, -) - - -class DateScalar(GraphQLScalar[date]): - @classmethod - def serialize(cls, value): - if isinstance(value, cls): - return str(value.unwrap()) - - return str(value) - - -def test_scalar_field_returning_scalar_instance(assert_schema_equals): - class QueryType(GraphQLObject): - date: DateScalar - - @GraphQLObject.resolver("date") - def resolve_date(*_) -> DateScalar: - return DateScalar(date(1989, 10, 30)) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - scalar Date - - type Query { - date: Date! - } - """, - ) - - result = graphql_sync(schema, "{ date }") - - assert not result.errors - assert result.data == {"date": "1989-10-30"} - - -def test_scalar_field_returning_scalar_wrapped_type(assert_schema_equals): - class QueryType(GraphQLObject): - scalar_date: DateScalar - - @GraphQLObject.resolver("scalar_date", graphql_type=DateScalar) - def resolve_date(*_) -> date: - return date(1989, 10, 30) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - scalar Date - - type Query { - scalarDate: Date! - } - """, - ) - - result = graphql_sync(schema, "{ scalarDate }") - - assert not result.errors - assert result.data == {"scalarDate": "1989-10-30"} - - -class SchemaDateScalar(GraphQLScalar[date]): - __schema__ = gql("scalar Date") - - @classmethod - def serialize(cls, value): - if isinstance(value, cls): - return str(value.unwrap()) - - return str(value) - - -def test_schema_scalar_field_returning_scalar_instance(assert_schema_equals): - class QueryType(GraphQLObject): - date: SchemaDateScalar - - @GraphQLObject.resolver("date") - def resolve_date(*_) -> SchemaDateScalar: - return SchemaDateScalar(date(1989, 10, 30)) - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - scalar Date - - type Query { - date: Date! - } - """, - ) - - result = graphql_sync(schema, "{ date }") - - assert not result.errors - assert result.data == {"date": "1989-10-30"} diff --git a/tests_next/test_subscription_type.py b/tests_next/test_subscription_type.py deleted file mode 100644 index b8bd2a5..0000000 --- a/tests_next/test_subscription_type.py +++ /dev/null @@ -1,754 +0,0 @@ -from typing import List - -from ariadne import gql -from graphql import subscribe, parse -import pytest -from ariadne_graphql_modules.next import ( - GraphQLID, - GraphQLObject, - GraphQLSubscription, - GraphQLUnion, - make_executable_schema, -) - - -class Message(GraphQLObject): - id: GraphQLID - content: str - author: str - - -class User(GraphQLObject): - id: GraphQLID - username: str - - -class Notification(GraphQLUnion): - __types__ = [Message, User] - - -@pytest.mark.asyncio -async def test_basic_subscription_without_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - message_added: Message - - @GraphQLSubscription.source("message_added") - async def message_added_generator(obj, info): - while True: - yield {"id": "some_id", "content": "message", "author": "Anon"} - - @GraphQLSubscription.resolver("message_added", graphql_type=Message) - async def resolve_message_added(message, info): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded: Message! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse("subscription { messageAdded {id content author} }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messageAdded": {"id": "some_id", "content": "message", "author": "Anon"} - } - - -@pytest.mark.asyncio -async def test_subscription_with_arguments_without_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - message_added: Message - - @GraphQLSubscription.source( - "message_added", - args={"channel": GraphQLObject.argument(description="Lorem ipsum.")}, - ) - async def message_added_generator(obj, info, channel: GraphQLID): - while True: - yield { - "id": "some_id", - "content": f"message_{channel}", - "author": "Anon", - } - - @GraphQLSubscription.resolver( - "message_added", - graphql_type=Message, - ) - async def resolve_message_added(message, *_, channel: GraphQLID): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded( - \"\"\"Lorem ipsum.\"\"\" - channel: ID! - ): Message! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse('subscription { messageAdded(channel: "123") {id content author} }') - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messageAdded": {"id": "some_id", "content": "message_123", "author": "Anon"} - } - - -@pytest.mark.asyncio -async def test_multiple_supscriptions_without_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - message_added: Message - user_joined: User - - @GraphQLSubscription.source( - "message_added", - args={"channel": GraphQLObject.argument(description="Lorem ipsum.")}, - ) - async def message_added_generator(obj, info, channel: GraphQLID): - while True: - yield { - "id": "some_id", - "content": f"message_{channel}", - "author": "Anon", - } - - @GraphQLSubscription.resolver( - "message_added", - graphql_type=Message, - ) - async def resolve_message_added(message, *_, channel: GraphQLID): - return message - - @GraphQLSubscription.source( - "user_joined", - ) - async def user_joined_generator(obj, info): - while True: - yield { - "id": "some_id", - "username": "username", - } - - @GraphQLSubscription.resolver( - "user_joined", - graphql_type=Message, - ) - async def resolve_user_joined(user, *_): - return user - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded( - \"\"\"Lorem ipsum.\"\"\" - channel: ID! - ): Message! - userJoined: User! - } - - type Message { - id: ID! - content: String! - author: String! - } - - type User { - id: ID! - username: String! - } - """, - ) - - query = parse("subscription { userJoined {id username} }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == {"userJoined": {"id": "some_id", "username": "username"}} - - -@pytest.mark.asyncio -async def test_subscription_with_complex_data_without_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - messages_in_channel: List[Message] - - @GraphQLSubscription.source( - "messages_in_channel", - args={"channel_id": GraphQLObject.argument(description="Lorem ipsum.")}, - ) - async def message_added_generator(obj, info, channel_id: GraphQLID): - while True: - yield [ - { - "id": "some_id", - "content": f"message_{channel_id}", - "author": "Anon", - } - ] - - @GraphQLSubscription.resolver( - "messages_in_channel", - graphql_type=Message, - ) - async def resolve_message_added(message, *_, channel_id: GraphQLID): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messagesInChannel( - \"\"\"Lorem ipsum.\"\"\" - channelId: ID! - ): [Message!]! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse( - 'subscription { messagesInChannel(channelId: "123") {id content author} }' - ) - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messagesInChannel": [ - {"id": "some_id", "content": "message_123", "author": "Anon"} - ] - } - - -@pytest.mark.asyncio -async def test_subscription_with_union_without_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - notification_received: Notification - - @GraphQLSubscription.source( - "notification_received", - ) - async def message_added_generator(obj, info): - while True: - yield Message(id=1, content="content", author="anon") - - @GraphQLSubscription.resolver( - "notification_received", - ) - async def resolve_message_added(message, *_): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - notificationReceived: Notification! - } - - union Notification = Message | User - - type Message { - id: ID! - content: String! - author: String! - } - - type User { - id: ID! - username: String! - } - """, - ) - - query = parse("subscription { notificationReceived { ... on Message { id } } }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == {"notificationReceived": {"id": "1"}} - - -@pytest.mark.asyncio -async def test_basic_subscription_with_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - __schema__ = gql( - """ - type Subscription { - messageAdded: Message! - } - """ - ) - - @GraphQLSubscription.source("messageAdded") - async def message_added_generator(obj, info): - while True: - yield {"id": "some_id", "content": "message", "author": "Anon"} - - @GraphQLSubscription.resolver("messageAdded", graphql_type=Message) - async def resolve_message_added(message, info): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType, Message) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded: Message! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse("subscription { messageAdded {id content author} }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messageAdded": {"id": "some_id", "content": "message", "author": "Anon"} - } - - -@pytest.mark.asyncio -async def test_subscription_with_arguments_with_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - __schema__ = gql( - """ - type Subscription { - messageAdded(channel: ID!): Message! - } - """ - ) - - @GraphQLSubscription.source( - "messageAdded", - ) - async def message_added_generator(obj, info, channel: GraphQLID): - while True: - yield { - "id": "some_id", - "content": f"message_{channel}", - "author": "Anon", - } - - @GraphQLSubscription.resolver( - "messageAdded", - graphql_type=Message, - ) - async def resolve_message_added(message, *_, channel: GraphQLID): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType, Message) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded(channel: ID!): Message! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse('subscription { messageAdded(channel: "123") {id content author} }') - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messageAdded": {"id": "some_id", "content": "message_123", "author": "Anon"} - } - - -@pytest.mark.asyncio -async def test_multiple_supscriptions_with_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - __schema__ = gql( - """ - type Subscription { - messageAdded: Message! - userJoined: User! - } - """ - ) - - @GraphQLSubscription.source( - "messageAdded", - ) - async def message_added_generator(obj, info): - while True: - yield { - "id": "some_id", - "content": "message", - "author": "Anon", - } - - @GraphQLSubscription.resolver( - "messageAdded", - ) - async def resolve_message_added(message, *_): - return message - - @GraphQLSubscription.source( - "userJoined", - ) - async def user_joined_generator(obj, info): - while True: - yield { - "id": "some_id", - "username": "username", - } - - @GraphQLSubscription.resolver( - "userJoined", - ) - async def resolve_user_joined(user, *_): - return user - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType, Message, User) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messageAdded: Message! - userJoined: User! - } - - type Message { - id: ID! - content: String! - author: String! - } - - type User { - id: ID! - username: String! - } - """, - ) - - query = parse("subscription { userJoined {id username} }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == {"userJoined": {"id": "some_id", "username": "username"}} - - -@pytest.mark.asyncio -async def test_subscription_with_complex_data_with_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - __schema__ = gql( - """ - type Subscription { - messagesInChannel(channelId: ID!): [Message!]! - } - """ - ) - - @GraphQLSubscription.source( - "messagesInChannel", - ) - async def message_added_generator(obj, info, channelId: GraphQLID): - while True: - yield [ - { - "id": "some_id", - "content": f"message_{channelId}", - "author": "Anon", - } - ] - - @GraphQLSubscription.resolver( - "messagesInChannel", - ) - async def resolve_message_added(message, *_, channelId: GraphQLID): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType, Message) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - messagesInChannel(channelId: ID!): [Message!]! - } - - type Message { - id: ID! - content: String! - author: String! - } - """, - ) - - query = parse( - 'subscription { messagesInChannel(channelId: "123") {id content author} }' - ) - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == { - "messagesInChannel": [ - {"id": "some_id", "content": "message_123", "author": "Anon"} - ] - } - - -@pytest.mark.asyncio -async def test_subscription_with_union_with_schema(assert_schema_equals): - class SubscriptionType(GraphQLSubscription): - __schema__ = gql( - """ - type Subscription { - notificationReceived: Notification! - } - """ - ) - - @GraphQLSubscription.source( - "notificationReceived", - ) - async def message_added_generator(obj, info): - while True: - yield Message(id=1, content="content", author="anon") - - @GraphQLSubscription.resolver( - "notificationReceived", - ) - async def resolve_message_added(message, *_): - return message - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=str) - def search_sth(*_) -> str: - return "search" - - schema = make_executable_schema(QueryType, SubscriptionType, Notification) - - assert_schema_equals( - schema, - """ - type Query { - searchSth: String! - } - - type Subscription { - notificationReceived: Notification! - } - - union Notification = Message | User - - type Message { - id: ID! - content: String! - author: String! - } - - type User { - id: ID! - username: String! - } - """, - ) - - query = parse("subscription { notificationReceived { ... on Message { id } } }") - sub = await subscribe(schema, query) - - # Ensure the subscription is an async iterator - assert hasattr(sub, "__aiter__") - - # Fetch the first result - result = await sub.__anext__() - - # Validate the result - assert not result.errors - assert result.data == {"notificationReceived": {"id": "1"}} diff --git a/tests_next/test_union_type.py b/tests_next/test_union_type.py deleted file mode 100644 index 6d20267..0000000 --- a/tests_next/test_union_type.py +++ /dev/null @@ -1,223 +0,0 @@ -from typing import List, Union - -from graphql import graphql_sync - -from ariadne_graphql_modules.next import ( - GraphQLID, - GraphQLObject, - GraphQLUnion, - make_executable_schema, -) - - -class UserType(GraphQLObject): - id: GraphQLID - username: str - - -class CommentType(GraphQLObject): - id: GraphQLID - content: str - - -def test_union_field_returning_object_instance(assert_schema_equals): - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(id=1, username="Bob"), - CommentType(id=2, content="Hello World!"), - ] - - schema = make_executable_schema(QueryType) - - assert_schema_equals( - schema, - """ - type Query { - search: [Result!]! - } - - union Result = User | Comment - - type User { - id: ID! - username: String! - } - - type Comment { - id: ID! - content: String! - } - """, - ) - - result = graphql_sync( - schema, - """ - { - search { - ... on User { - id - username - } - ... on Comment { - id - content - } - } - } - """, - ) - - assert not result.errors - assert result.data == { - "search": [ - {"id": "1", "username": "Bob"}, - {"id": "2", "content": "Hello World!"}, - ] - } - - -def test_union_field_returning_empty_list(): - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [] - - schema = make_executable_schema(QueryType) - - result = graphql_sync( - schema, - """ - { - search { - ... on User { - id - username - } - ... on Comment { - id - content - } - } - } - """, - ) - assert not result.errors - assert result.data == {"search": []} - - -def test_union_field_with_invalid_type_access(assert_schema_equals): - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(id=1, username="Bob"), - "InvalidType", - ] - - schema = make_executable_schema(QueryType) - - result = graphql_sync( - schema, - """ - { - search { - ... on User { - id - username - } - ... on Comment { - id - content - } - } - } - """, - ) - assert result.errors - assert "InvalidType" in str(result.errors) - - -def test_serialization_error_handling(assert_schema_equals): - class InvalidType: - def __init__(self, value): - self.value = value - - class ResultType(GraphQLUnion): - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[ResultType]) - def search(*_) -> List[Union[UserType, CommentType, InvalidType]]: - return [InvalidType("This should cause an error")] - - schema = make_executable_schema(QueryType) - - result = graphql_sync( - schema, - """ - { - search { - ... on User { - id - username - } - } - } - """, - ) - assert result.errors - - -def test_union_with_schema_definition(assert_schema_equals): - class SearchResultUnion(GraphQLUnion): - __schema__ = """ - union SearchResult = User | Comment - """ - __types__ = [UserType, CommentType] - - class QueryType(GraphQLObject): - @GraphQLObject.field(graphql_type=List[SearchResultUnion]) - def search(*_) -> List[Union[UserType, CommentType]]: - return [ - UserType(id="1", username="Alice"), - CommentType(id="2", content="Test post"), - ] - - schema = make_executable_schema(QueryType, SearchResultUnion) - - result = graphql_sync( - schema, - """ - { - search { - ... on User { - id - username - } - ... on Comment { - id - content - } - } - } - """, - ) - assert not result.errors - assert result.data == { - "search": [ - {"id": "1", "username": "Alice"}, - {"id": "2", "content": "Test post"}, - ] - } diff --git a/tests_next/__init__.py b/tests_v1/__init__.py similarity index 100% rename from tests_next/__init__.py rename to tests_v1/__init__.py diff --git a/tests_v1/conftest.py b/tests_v1/conftest.py new file mode 100644 index 0000000..0f16709 --- /dev/null +++ b/tests_v1/conftest.py @@ -0,0 +1,12 @@ +from pathlib import Path +import pytest + + +@pytest.fixture(scope="session") +def datadir() -> Path: + return Path(__file__).parent / "snapshots" + + +@pytest.fixture(scope="session") +def original_datadir() -> Path: + return Path(__file__).parent / "snapshots" diff --git a/tests/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.obtained.yml b/tests_v1/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.obtained.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.obtained.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.obtained.yml diff --git a/tests/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.yml b/tests_v1/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_schema_str_contains_multiple_types.yml diff --git a/tests/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.obtained.yml b/tests_v1/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.obtained.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.obtained.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.obtained.yml diff --git a/tests/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.yml b/tests_v1/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_when_schema_str_has_invalid_syntax.yml diff --git a/tests/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.obtained.yml b/tests_v1/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.obtained.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.obtained.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.obtained.yml diff --git a/tests/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.yml b/tests_v1/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.yml similarity index 100% rename from tests/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.yml rename to tests_v1/snapshots/test_definition_parser_raises_error_when_schema_type_is_invalid.yml diff --git a/tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.yml b/tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.yml rename to tests_v1/snapshots/test_directive_type_raises_attribute_error_when_defined_without_visitor.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_directive_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_enum_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.obtained.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.obtained.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.obtained.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.obtained.yml diff --git a/tests/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.yml b/tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.yml similarity index 100% rename from tests/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.yml rename to tests_v1/snapshots/test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition.yml diff --git a/tests/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.obtained.yml b/tests_v1/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.obtained.yml similarity index 100% rename from tests/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.obtained.yml rename to tests_v1/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.obtained.yml diff --git a/tests/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.yml b/tests_v1/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.yml similarity index 100% rename from tests/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.yml rename to tests_v1/snapshots/test_executable_schema_raises_value_error_if_merged_types_define_same_field.yml diff --git a/tests/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_input_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_extended_dependency.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_field_type_dependency.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_fields.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_fields.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_fields.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_defined_without_fields.yml b/tests_v1/snapshots/test_input_type_raises_error_when_defined_without_fields.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_defined_without_fields.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_defined_without_fields.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml b/tests_v1/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml diff --git a/tests/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.yml b/tests_v1/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.yml similarity index 100% rename from tests/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.yml rename to tests_v1/snapshots/test_input_type_raises_error_when_extended_dependency_is_wrong_type.yml diff --git a/tests/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_interface_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_argument_type_dependency.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_extended_dependency.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_fields.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_fields.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_fields.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_fields.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_fields.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_fields.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_fields.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_defined_without_return_type_dependency.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml diff --git a/tests/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.yml b/tests_v1/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.yml similarity index 100% rename from tests/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.yml rename to tests_v1/snapshots/test_interface_type_raises_error_when_extended_dependency_is_wrong_type.yml diff --git a/tests/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_mutation_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_for_different_type_name.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_fields.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_with_nonexistant_args.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_callable_resolve_mutation_attr.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_fields.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_fields.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_fields.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_fields.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_fields.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_fields.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_fields.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_resolve_mutation_attr.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.obtained.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.obtained.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.obtained.yml diff --git a/tests/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.yml b/tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.yml similarity index 100% rename from tests/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.yml rename to tests_v1/snapshots/test_mutation_type_raises_error_when_defined_without_return_type_dependency.yml diff --git a/tests/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_object_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_argument_type_dependency.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_extended_dependency.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_fields.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_fields.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_fields.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_fields.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_fields.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_fields.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_fields.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.yml b/tests_v1/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_defined_without_return_type_dependency.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml b/tests_v1/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml diff --git a/tests/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.yml b/tests_v1/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.yml similarity index 100% rename from tests/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.yml rename to tests_v1/snapshots/test_object_type_raises_error_when_extended_dependency_is_wrong_type.yml diff --git a/tests/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_scalar_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_scalar_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_subscription_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_argument_type_dependency.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_extended_dependency.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_fields.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_fields.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_fields.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_fields.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_fields.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_fields.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_fields.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_fields.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_defined_without_return_type_dependency.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml diff --git a/tests/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.yml b/tests_v1/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.yml similarity index 100% rename from tests/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.yml rename to tests_v1/snapshots/test_subscription_type_raises_error_when_extended_dependency_is_wrong_type.yml diff --git a/tests/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.obtained.yml b/tests_v1/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.yml b/tests_v1/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.yml rename to tests_v1/snapshots/test_union_type_raises_attribute_error_when_defined_without_schema.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_str.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_invalid_schema_type.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_with_multiple_types_schema.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_without_extended_dependency.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.yml b/tests_v1/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_defined_without_member_type_dependency.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml b/tests_v1/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.obtained.yml diff --git a/tests/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.yml b/tests_v1/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.yml similarity index 100% rename from tests/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.yml rename to tests_v1/snapshots/test_union_type_raises_error_when_extended_dependency_is_wrong_type.yml diff --git a/tests/test_collection_type.py b/tests_v1/test_collection_type.py similarity index 100% rename from tests/test_collection_type.py rename to tests_v1/test_collection_type.py diff --git a/tests/test_convert_case.py b/tests_v1/test_convert_case.py similarity index 100% rename from tests/test_convert_case.py rename to tests_v1/test_convert_case.py diff --git a/tests/test_definition_parser.py b/tests_v1/test_definition_parser.py similarity index 100% rename from tests/test_definition_parser.py rename to tests_v1/test_definition_parser.py diff --git a/tests/test_directive_type.py b/tests_v1/test_directive_type.py similarity index 100% rename from tests/test_directive_type.py rename to tests_v1/test_directive_type.py diff --git a/tests_v1/test_enum_type.py b/tests_v1/test_enum_type.py new file mode 100644 index 0000000..7ea4585 --- /dev/null +++ b/tests_v1/test_enum_type.py @@ -0,0 +1,304 @@ +from enum import Enum + +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, graphql_sync + +from ariadne_graphql_modules import ( + DirectiveType, + EnumType, + ObjectType, + make_executable_schema, +) + + +def test_enum_type_raises_attribute_error_when_defined_without_schema(data_regression): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + pass + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_defined_with_invalid_schema_type(data_regression): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_defined_with_invalid_schema_str(data_regression): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = "enom UserRole" + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = "scalar UserRole" + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + + enum Category { + CATEGORY + LINK + } + """ + + data_regression.check(str(err.value)) + + +def test_enum_type_extracts_graphql_name(): + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + + assert UserRoleEnum.graphql_name == "UserRole" + + +def test_enum_type_can_be_extended_with_new_values(): + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + + class ExtendUserRoleEnum(EnumType): + __schema__ = """ + extend enum UserRole { + MVP + } + """ + __requires__ = [UserRoleEnum] + + +def test_enum_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on ENUM" + __visitor__ = SchemaDirectiveVisitor + + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + + class ExtendUserRoleEnum(EnumType): + __schema__ = "extend enum UserRole @example" + __requires__ = [UserRoleEnum, ExampleDirective] + + +class BaseQueryType(ObjectType): + __abstract__ = True + __schema__ = """ + type Query { + enumToRepr(enum: UserRole = USER): String! + reprToEnum: UserRole! + } + """ + __aliases__ = { + "enumToRepr": "enum_repr", + } + + @staticmethod + def resolve_enum_repr(*_, enum) -> str: + return repr(enum) + + +def make_test_schema(enum_type): + class QueryType(BaseQueryType): + __requires__ = [enum_type] + + return make_executable_schema(QueryType) + + +def test_enum_type_can_be_defined_with_dict_mapping(): + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = { + "USER": 0, + "MOD": 1, + "ADMIN": 2, + } + + schema = make_test_schema(UserRoleEnum) + + # Specfied enum value is reversed + result = graphql_sync(schema, "{ enumToRepr(enum: MOD) }") + assert result.data["enumToRepr"] == "1" + + # Default enum value is reversed + result = graphql_sync(schema, "{ enumToRepr }") + assert result.data["enumToRepr"] == "0" + + # Python value is converted to enum + result = graphql_sync(schema, "{ reprToEnum }", root_value={"reprToEnum": 2}) + assert result.data["reprToEnum"] == "ADMIN" + + +def test_enum_type_raises_error_when_dict_mapping_misses_items_from_definition( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = { + "USER": 0, + "MODERATOR": 1, + "ADMIN": 2, + } + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_dict_mapping_has_extra_items_not_in_definition( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = { + "USER": 0, + "REVIEW": 1, + "MOD": 2, + "ADMIN": 3, + } + + data_regression.check(str(err.value)) + + +def test_enum_type_can_be_defined_with_str_enum_mapping(): + class RoleEnum(str, Enum): + USER = "user" + MOD = "moderator" + ADMIN = "administrator" + + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = RoleEnum + + schema = make_test_schema(UserRoleEnum) + + # Specfied enum value is reversed + result = graphql_sync(schema, "{ enumToRepr(enum: MOD) }") + assert result.data["enumToRepr"] == repr(RoleEnum.MOD) + + # Default enum value is reversed + result = graphql_sync(schema, "{ enumToRepr }") + assert result.data["enumToRepr"] == repr(RoleEnum.USER) + + # Python value is converted to enum + result = graphql_sync( + schema, "{ reprToEnum }", root_value={"reprToEnum": "administrator"} + ) + assert result.data["reprToEnum"] == "ADMIN" + + +def test_enum_type_raises_error_when_enum_mapping_misses_items_from_definition( + data_regression, +): + class RoleEnum(str, Enum): + USER = "user" + MODERATOR = "moderator" + ADMIN = "administrator" + + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = RoleEnum + + data_regression.check(str(err.value)) + + +def test_enum_type_raises_error_when_enum_mapping_has_extra_items_not_in_definition( + data_regression, +): + class RoleEnum(str, Enum): + USER = "user" + REVIEW = "review" + MOD = "moderator" + ADMIN = "administrator" + + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserRoleEnum(EnumType): + __schema__ = """ + enum UserRole { + USER + MOD + ADMIN + } + """ + __enum__ = RoleEnum + + data_regression.check(str(err.value)) diff --git a/tests/test_executable_schema.py b/tests_v1/test_executable_schema.py similarity index 100% rename from tests/test_executable_schema.py rename to tests_v1/test_executable_schema.py diff --git a/tests/test_executable_schema_compat.py b/tests_v1/test_executable_schema_compat.py similarity index 100% rename from tests/test_executable_schema_compat.py rename to tests_v1/test_executable_schema_compat.py diff --git a/tests_v1/test_input_type.py b/tests_v1/test_input_type.py new file mode 100644 index 0000000..daad5e0 --- /dev/null +++ b/tests_v1/test_input_type.py @@ -0,0 +1,293 @@ +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, graphql_sync + +from ariadne_graphql_modules import ( + DeferredType, + DirectiveType, + EnumType, + InputType, + InterfaceType, + ObjectType, + ScalarType, + make_executable_schema, +) + + +def test_input_type_raises_attribute_error_when_defined_without_schema(data_regression): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + pass + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_with_invalid_schema_type(data_regression): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_with_invalid_schema_str(data_regression): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = "inpet UserInput" + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + type User { + id: ID! + } + """ + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + input User + + input Group + """ + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_without_fields(data_regression): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = "input User" + + data_regression.check(str(err.value)) + + +def test_input_type_extracts_graphql_name(): + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + } + """ + + assert UserInput.graphql_name == "User" + + +def test_input_type_raises_error_when_defined_without_field_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + role: Role! + } + """ + + data_regression.check(str(err.value)) + + +def test_input_type_verifies_field_dependency(): + # pylint: disable=unused-variable + class RoleEnum(EnumType): + __schema__ = """ + enum Role { + USER + ADMIN + } + """ + + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + role: Role! + } + """ + __requires__ = [RoleEnum] + + +def test_input_type_verifies_circular_dependency(): + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + patron: User + } + """ + + +def test_input_type_verifies_circular_dependency_using_deferred_type(): + # pylint: disable=unused-variable + class GroupInput(InputType): + __schema__ = """ + input Group { + id: ID! + patron: User + } + """ + __requires__ = [DeferredType("User")] + + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + group: Group + } + """ + __requires__ = [GroupInput] + + +def test_input_type_can_be_extended_with_new_fields(): + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + } + """ + + class ExtendUserInput(InputType): + __schema__ = """ + extend input User { + name: String! + } + """ + __requires__ = [UserInput] + + +def test_input_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on INPUT_OBJECT" + __visitor__ = SchemaDirectiveVisitor + + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + } + """ + + class ExtendUserInput(InputType): + __schema__ = """ + extend input User @example + """ + __requires__ = [UserInput, ExampleDirective] + + +def test_input_type_raises_error_when_defined_without_extended_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExtendUserInput(InputType): + __schema__ = """ + extend input User { + name: String! + } + """ + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_extended_dependency_is_wrong_type( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface User { + id: ID! + } + """ + + class ExtendUserInput(InputType): + __schema__ = """ + extend input User { + name: String! + } + """ + __requires__ = [ExampleInterface] + + data_regression.check(str(err.value)) + + +def test_input_type_raises_error_when_defined_with_args_map_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserInput(InputType): + __schema__ = """ + input User { + id: ID! + } + """ + __args__ = { + "fullName": "full_name", + } + + data_regression.check(str(err.value)) + + +class UserInput(InputType): + __schema__ = """ + input UserInput { + id: ID! + fullName: String! + } + """ + __args__ = { + "fullName": "full_name", + } + + +class GenericScalar(ScalarType): + __schema__ = "scalar Generic" + + +class QueryType(ObjectType): + __schema__ = """ + type Query { + reprInput(input: UserInput): Generic! + } + """ + __aliases__ = {"reprInput": "repr_input"} + __requires__ = [GenericScalar, UserInput] + + @staticmethod + def resolve_repr_input(*_, input): # pylint: disable=redefined-builtin + return input + + +schema = make_executable_schema(QueryType) + + +def test_input_type_maps_args_to_python_dict_keys(): + result = graphql_sync(schema, '{ reprInput(input: {id: "1", fullName: "Alice"}) }') + assert result.data == { + "reprInput": {"id": "1", "full_name": "Alice"}, + } diff --git a/tests_v1/test_interface_type.py b/tests_v1/test_interface_type.py new file mode 100644 index 0000000..be3637d --- /dev/null +++ b/tests_v1/test_interface_type.py @@ -0,0 +1,462 @@ +from dataclasses import dataclass + +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, graphql_sync + +from ariadne_graphql_modules import ( + DeferredType, + DirectiveType, + InterfaceType, + ObjectType, + make_executable_schema, +) + + +def test_interface_type_raises_attribute_error_when_defined_without_schema( + data_regression, +): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + pass + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_invalid_schema_type( + data_regression, +): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_invalid_schema_str( + data_regression, +): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = "interfaco Example" + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = "type Example" + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example + + interface Other + """ + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_without_fields(data_regression): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = "interface Example" + + data_regression.check(str(err.value)) + + +def test_interface_type_extracts_graphql_name(): + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + } + """ + + assert ExampleInterface.graphql_name == "Example" + + +def test_interface_type_raises_error_when_defined_without_return_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + group: Group + groups: [Group!] + } + """ + + data_regression.check(str(err.value)) + + +def test_interface_type_verifies_field_dependency(): + # pylint: disable=unused-variable + class GroupType(ObjectType): + __schema__ = """ + type Group { + id: ID! + } + """ + + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + group: Group + groups: [Group!] + } + """ + __requires__ = [GroupType] + + +def test_interface_type_verifies_circural_dependency(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + parent: Example + } + """ + + +def test_interface_type_raises_error_when_defined_without_argument_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + actions(input: UserInput): [String!]! + } + """ + + data_regression.check(str(err.value)) + + +def test_interface_type_verifies_circular_dependency_using_deferred_type(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + users: [User] + } + """ + __requires__ = [DeferredType("User")] + + class UserType(ObjectType): + __schema__ = """ + type User { + roles: [Example] + } + """ + __requires__ = [ExampleInterface] + + +def test_interface_type_can_be_extended_with_new_fields(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + } + """ + + class ExtendExampleInterface(InterfaceType): + __schema__ = """ + extend interface Example { + name: String + } + """ + __requires__ = [ExampleInterface] + + +def test_interface_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on INTERFACE" + __visitor__ = SchemaDirectiveVisitor + + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + } + """ + + class ExtendExampleInterface(InterfaceType): + __schema__ = """ + extend interface Example @example + """ + __requires__ = [ExampleInterface, ExampleDirective] + + +def test_interface_type_can_be_extended_with_other_interface(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + } + """ + + class OtherInterface(InterfaceType): + __schema__ = """ + interface Other { + depth: Int! + } + """ + + class ExtendExampleInterface(InterfaceType): + __schema__ = """ + extend interface Example implements Other + """ + __requires__ = [ExampleInterface, OtherInterface] + + +def test_interface_type_raises_error_when_defined_without_extended_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExtendExampleInterface(ObjectType): + __schema__ = """ + extend interface Example { + name: String + } + """ + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_extended_dependency_is_wrong_type( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleType(ObjectType): + __schema__ = """ + type Example { + id: ID! + } + """ + + class ExampleInterface(InterfaceType): + __schema__ = """ + extend interface Example { + name: String + } + """ + __requires__ = [ExampleType] + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_alias_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface User { + name: String + } + """ + __aliases__ = { + "joinedDate": "joined_date", + } + + data_regression.check(str(err.value)) + + +def test_interface_type_raises_error_when_defined_with_resolver_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface User { + name: String + } + """ + + @staticmethod + def resolve_group(*_): + return None + + data_regression.check(str(err.value)) + + +@dataclass +class User: + id: int + name: str + summary: str + + +@dataclass +class Comment: + id: int + message: str + summary: str + + +class ResultInterface(InterfaceType): + __schema__ = """ + interface Result { + summary: String! + score: Int! + } + """ + + @staticmethod + def resolve_type(instance, *_): + if isinstance(instance, Comment): + return "Comment" + + if isinstance(instance, User): + return "User" + + return None + + @staticmethod + def resolve_score(*_): + return 42 + + +class UserType(ObjectType): + __schema__ = """ + type User implements Result { + id: ID! + name: String! + summary: String! + score: Int! + } + """ + __requires__ = [ResultInterface] + + +class CommentType(ObjectType): + __schema__ = """ + type Comment implements Result { + id: ID! + message: String! + summary: String! + score: Int! + } + """ + __requires__ = [ResultInterface] + + @staticmethod + def resolve_score(*_): + return 16 + + +class QueryType(ObjectType): + __schema__ = """ + type Query { + results: [Result!]! + } + """ + __requires__ = [ResultInterface] + + @staticmethod + def resolve_results(*_): + return [ + User(id=1, name="Alice", summary="Summary for Alice"), + Comment(id=1, message="Hello world!", summary="Summary for comment"), + ] + + +schema = make_executable_schema(QueryType, UserType, CommentType) + + +def test_interface_type_binds_type_resolver(): + query = """ + query { + results { + ... on User { + __typename + id + name + summary + } + ... on Comment { + __typename + id + message + summary + } + } + } + """ + + result = graphql_sync(schema, query) + assert result.data == { + "results": [ + { + "__typename": "User", + "id": "1", + "name": "Alice", + "summary": "Summary for Alice", + }, + { + "__typename": "Comment", + "id": "1", + "message": "Hello world!", + "summary": "Summary for comment", + }, + ], + } + + +def test_interface_type_binds_field_resolvers_to_implementing_types_fields(): + query = """ + query { + results { + ... on User { + __typename + score + } + ... on Comment { + __typename + score + } + } + } + """ + + result = graphql_sync(schema, query) + assert result.data == { + "results": [ + { + "__typename": "User", + "score": 42, + }, + { + "__typename": "Comment", + "score": 16, + }, + ], + } diff --git a/tests/test_mutation_type.py b/tests_v1/test_mutation_type.py similarity index 100% rename from tests/test_mutation_type.py rename to tests_v1/test_mutation_type.py diff --git a/tests_v1/test_object_type.py b/tests_v1/test_object_type.py new file mode 100644 index 0000000..8adf0e6 --- /dev/null +++ b/tests_v1/test_object_type.py @@ -0,0 +1,410 @@ +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, graphql_sync + +from ariadne_graphql_modules import ( + DeferredType, + DirectiveType, + InterfaceType, + ObjectType, + make_executable_schema, +) + + +def test_object_type_raises_attribute_error_when_defined_without_schema( + data_regression, +): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + pass + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_invalid_schema_type( + data_regression, +): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_invalid_schema_str(data_regression): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = "typo User" + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = "scalar DateTime" + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User + + type Group + """ + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_without_fields(data_regression): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = "type User" + + data_regression.check(str(err.value)) + + +def test_object_type_extracts_graphql_name(): + class GroupType(ObjectType): + __schema__ = """ + type Group { + id: ID! + } + """ + + assert GroupType.graphql_name == "Group" + + +def test_object_type_accepts_all_builtin_scalar_types(): + # pylint: disable=unused-variable + class FancyObjectType(ObjectType): + __schema__ = """ + type FancyObject { + id: ID! + someInt: Int! + someFloat: Float! + someBoolean: Boolean! + someString: String! + } + """ + + +def test_object_type_raises_error_when_defined_without_return_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + group: Group + groups: [Group!] + } + """ + + data_regression.check(str(err.value)) + + +def test_object_type_verifies_field_dependency(): + # pylint: disable=unused-variable + class GroupType(ObjectType): + __schema__ = """ + type Group { + id: ID! + } + """ + + class UserType(ObjectType): + __schema__ = """ + type User { + group: Group + groups: [Group!] + } + """ + __requires__ = [GroupType] + + +def test_object_type_verifies_circular_dependency(): + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + follows: User + } + """ + + +def test_object_type_raises_error_when_defined_without_argument_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + actions(input: UserInput): [String!]! + } + """ + + data_regression.check(str(err.value)) + + +def test_object_type_verifies_circular_dependency_using_deferred_type(): + # pylint: disable=unused-variable + class GroupType(ObjectType): + __schema__ = """ + type Group { + id: ID! + users: [User] + } + """ + __requires__ = [DeferredType("User")] + + class UserType(ObjectType): + __schema__ = """ + type User { + group: Group + } + """ + __requires__ = [GroupType] + + +def test_object_type_can_be_extended_with_new_fields(): + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + id: ID! + } + """ + + class ExtendUserType(ObjectType): + __schema__ = """ + extend type User { + name: String + } + """ + __requires__ = [UserType] + + +def test_object_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on OBJECT" + __visitor__ = SchemaDirectiveVisitor + + class UserType(ObjectType): + __schema__ = """ + type User { + id: ID! + } + """ + + class ExtendUserType(ObjectType): + __schema__ = """ + extend type User @example + """ + __requires__ = [UserType, ExampleDirective] + + +def test_object_type_can_be_extended_with_interface(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Interface { + id: ID! + } + """ + + class UserType(ObjectType): + __schema__ = """ + type User { + id: ID! + } + """ + + class ExtendUserType(ObjectType): + __schema__ = """ + extend type User implements Interface + """ + __requires__ = [UserType, ExampleInterface] + + +def test_object_type_raises_error_when_defined_without_extended_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExtendUserType(ObjectType): + __schema__ = """ + extend type User { + name: String + } + """ + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_extended_dependency_is_wrong_type( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Example { + id: ID! + } + """ + + class ExampleType(ObjectType): + __schema__ = """ + extend type Example { + name: String + } + """ + __requires__ = [ExampleInterface] + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_alias_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + name: String + } + """ + __aliases__ = { + "joinedDate": "joined_date", + } + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_resolver_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + name: String + } + """ + + @staticmethod + def resolve_group(*_): + return None + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + name: String + } + """ + __fields_args__ = {"group": {}} + + data_regression.check(str(err.value)) + + +def test_object_type_raises_error_when_defined_with_field_args_for_nonexisting_arg( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UserType(ObjectType): + __schema__ = """ + type User { + name: String + } + """ + __fields_args__ = {"name": {"arg": "arg2"}} + + data_regression.check(str(err.value)) + + +class QueryType(ObjectType): + __schema__ = """ + type Query { + field: String! + other: String! + firstField: String! + secondField: String! + fieldWithArg(someArg: String): String! + } + """ + __aliases__ = { + "firstField": "first_field", + "secondField": "second_field", + "fieldWithArg": "field_with_arg", + } + __fields_args__ = {"fieldWithArg": {"someArg": "some_arg"}} + + @staticmethod + def resolve_other(*_): + return "Word Up!" + + @staticmethod + def resolve_second_field(obj, *_): + return "Obj: %s" % obj["secondField"] + + @staticmethod + def resolve_field_with_arg(*_, some_arg): + return some_arg + + +schema = make_executable_schema(QueryType) + + +def test_object_resolves_field_with_default_resolver(): + result = graphql_sync(schema, "{ field }", root_value={"field": "Hello!"}) + assert result.data["field"] == "Hello!" + + +def test_object_resolves_field_with_custom_resolver(): + result = graphql_sync(schema, "{ other }") + assert result.data["other"] == "Word Up!" + + +def test_object_resolves_field_with_aliased_default_resolver(): + result = graphql_sync( + schema, "{ firstField }", root_value={"first_field": "Howdy?"} + ) + assert result.data["firstField"] == "Howdy?" + + +def test_object_resolves_field_with_aliased_custom_resolver(): + result = graphql_sync(schema, "{ secondField }", root_value={"secondField": "Hey!"}) + assert result.data["secondField"] == "Obj: Hey!" + + +def test_object_resolves_field_with_arg_out_name_customized(): + result = graphql_sync(schema, '{ fieldWithArg(someArg: "test") }') + assert result.data["fieldWithArg"] == "test" diff --git a/tests_v1/test_scalar_type.py b/tests_v1/test_scalar_type.py new file mode 100644 index 0000000..ec85340 --- /dev/null +++ b/tests_v1/test_scalar_type.py @@ -0,0 +1,287 @@ +from datetime import date, datetime + +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, StringValueNode, graphql_sync + +from ariadne_graphql_modules import ( + DirectiveType, + ObjectType, + ScalarType, + make_executable_schema, +) + + +def test_scalar_type_raises_attribute_error_when_defined_without_schema( + data_regression, +): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class DateScalar(ScalarType): + pass + + data_regression.check(str(err.value)) + + +def test_scalar_type_raises_error_when_defined_with_invalid_schema_type( + data_regression, +): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class DateScalar(ScalarType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_scalar_type_raises_error_when_defined_with_invalid_schema_str(data_regression): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class DateScalar(ScalarType): + __schema__ = "scalor Date" + + data_regression.check(str(err.value)) + + +def test_scalar_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class DateScalar(ScalarType): + __schema__ = "type DateTime" + + data_regression.check(str(err.value)) + + +def test_scalar_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class DateScalar(ScalarType): + __schema__ = """ + scalar Date + + scalar DateTime + """ + + data_regression.check(str(err.value)) + + +def test_scalar_type_extracts_graphql_name(): + class DateScalar(ScalarType): + __schema__ = "scalar Date" + + assert DateScalar.graphql_name == "Date" + + +def test_scalar_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on SCALAR" + __visitor__ = SchemaDirectiveVisitor + + class DateScalar(ScalarType): + __schema__ = "scalar Date" + + class ExtendDateScalar(ScalarType): + __schema__ = "extend scalar Date @example" + __requires__ = [DateScalar, ExampleDirective] + + +class DateReadOnlyScalar(ScalarType): + __schema__ = "scalar DateReadOnly" + + @staticmethod + def serialize(date): + return date.strftime("%Y-%m-%d") + + +class DateInputScalar(ScalarType): + __schema__ = "scalar DateInput" + + @staticmethod + def parse_value(formatted_date): + parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") + return parsed_datetime.date() + + @staticmethod + def parse_literal(ast, variable_values=None): # pylint: disable=unused-argument + if not isinstance(ast, StringValueNode): + raise ValueError() + + formatted_date = ast.value + parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") + return parsed_datetime.date() + + +class DefaultParserScalar(ScalarType): + __schema__ = "scalar DefaultParser" + + @staticmethod + def parse_value(value): + return type(value).__name__ + + +TEST_DATE = date(2006, 9, 13) +TEST_DATE_SERIALIZED = TEST_DATE.strftime("%Y-%m-%d") + + +class QueryType(ObjectType): + __schema__ = """ + type Query { + testSerialize: DateReadOnly! + testInput(value: DateInput!): Boolean! + testInputValueType(value: DefaultParser!): String! + } + """ + __requires__ = [ + DateReadOnlyScalar, + DateInputScalar, + DefaultParserScalar, + ] + __aliases__ = { + "testSerialize": "test_serialize", + "testInput": "test_input", + "testInputValueType": "test_input_value_type", + } + + @staticmethod + def resolve_test_serialize(*_): + return TEST_DATE + + @staticmethod + def resolve_test_input(*_, value): + assert value == TEST_DATE + return True + + @staticmethod + def resolve_test_input_value_type(*_, value): + return value + + +schema = make_executable_schema(QueryType) + + +def test_attempt_deserialize_str_literal_without_valid_date_raises_error(): + test_input = "invalid string" + result = graphql_sync(schema, '{ testInput(value: "%s") }' % test_input) + assert result.errors is not None + assert str(result.errors[0]).splitlines()[:1] == [ + "Expected value of type 'DateInput!', found \"invalid string\"; " + "time data 'invalid string' does not match format '%Y-%m-%d'" + ] + + +def test_attempt_deserialize_wrong_type_literal_raises_error(): + test_input = 123 + result = graphql_sync(schema, "{ testInput(value: %s) }" % test_input) + assert result.errors is not None + assert str(result.errors[0]).splitlines()[:1] == [ + "Expected value of type 'DateInput!', found 123; " + ] + + +def test_default_literal_parser_is_used_to_extract_value_str_from_ast_node(): + class ValueParserOnlyScalar(ScalarType): + __schema__ = "scalar DateInput" + + @staticmethod + def parse_value(formatted_date): + parsed_datetime = datetime.strptime(formatted_date, "%Y-%m-%d") + return parsed_datetime.date() + + class ValueParserOnlyQueryType(ObjectType): + __schema__ = """ + type Query { + parse(value: DateInput!): String! + } + """ + __requires__ = [ValueParserOnlyScalar] + + @staticmethod + def resolve_parse(*_, value): + return value + + schema = make_executable_schema(ValueParserOnlyQueryType) + result = graphql_sync(schema, """{ parse(value: "%s") }""" % TEST_DATE_SERIALIZED) + assert result.errors is None + assert result.data == {"parse": "2006-09-13"} + + +parametrized_query = """ + query parseValueTest($value: DateInput!) { + testInput(value: $value) + } +""" + + +def test_variable_with_valid_date_string_is_deserialized_to_python_date(): + variables = {"value": TEST_DATE_SERIALIZED} + result = graphql_sync(schema, parametrized_query, variable_values=variables) + assert result.errors is None + assert result.data == {"testInput": True} + + +def test_attempt_deserialize_str_variable_without_valid_date_raises_error(): + variables = {"value": "invalid string"} + result = graphql_sync(schema, parametrized_query, variable_values=variables) + assert result.errors is not None + assert str(result.errors[0]).splitlines()[:1] == [ + "Variable '$value' got invalid value 'invalid string'; " + "Expected type 'DateInput'. " + "time data 'invalid string' does not match format '%Y-%m-%d'" + ] + + +def test_attempt_deserialize_wrong_type_variable_raises_error(): + variables = {"value": 123} + result = graphql_sync(schema, parametrized_query, variable_values=variables) + assert result.errors is not None + assert str(result.errors[0]).splitlines()[:1] == [ + "Variable '$value' got invalid value 123; Expected type 'DateInput'. " + "strptime() argument 1 must be str, not int" + ] + + +def test_literal_string_is_deserialized_by_default_parser(): + result = graphql_sync(schema, '{ testInputValueType(value: "test") }') + assert result.errors is None + assert result.data == {"testInputValueType": "str"} + + +def test_literal_int_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: 123) }") + assert result.errors is None + assert result.data == {"testInputValueType": "int"} + + +def test_literal_float_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: 1.5) }") + assert result.errors is None + assert result.data == {"testInputValueType": "float"} + + +def test_literal_bool_true_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: true) }") + assert result.errors is None + assert result.data == {"testInputValueType": "bool"} + + +def test_literal_bool_false_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: false) }") + assert result.errors is None + assert result.data == {"testInputValueType": "bool"} + + +def test_literal_object_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: {}) }") + assert result.errors is None + assert result.data == {"testInputValueType": "dict"} + + +def test_literal_list_is_deserialized_by_default_parser(): + result = graphql_sync(schema, "{ testInputValueType(value: []) }") + assert result.errors is None + assert result.data == {"testInputValueType": "list"} diff --git a/tests_v1/test_subscription_type.py b/tests_v1/test_subscription_type.py new file mode 100644 index 0000000..c0067cf --- /dev/null +++ b/tests_v1/test_subscription_type.py @@ -0,0 +1,325 @@ +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, build_schema + +from ariadne_graphql_modules import ( + DirectiveType, + InterfaceType, + ObjectType, + SubscriptionType, +) + + +def test_subscription_type_raises_attribute_error_when_defined_without_schema( + data_regression, +): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + pass + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_invalid_schema_type( + data_regression, +): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_invalid_schema_str( + data_regression, +): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + __schema__ = "typo Subscription" + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + __schema__ = "scalar Subscription" + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_invalid_graphql_type_name( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + __schema__ = "type Other" + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_without_fields(data_regression): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class UsersSubscription(SubscriptionType): + __schema__ = "type Subscription" + + data_regression.check(str(err.value)) + + +def test_subscription_type_extracts_graphql_name(): + class UsersSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + thread: ID! + } + """ + + assert UsersSubscription.graphql_name == "Subscription" + + +def test_subscription_type_raises_error_when_defined_without_return_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: Chat + Chats: [Chat!] + } + """ + + data_regression.check(str(err.value)) + + +def test_subscription_type_verifies_field_dependency(): + # pylint: disable=unused-variable + class ChatType(ObjectType): + __schema__ = """ + type Chat { + id: ID! + } + """ + + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: Chat + Chats: [Chat!] + } + """ + __requires__ = [ChatType] + + +def test_subscription_type_raises_error_when_defined_without_argument_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat(input: ChannelInput): [String!]! + } + """ + + data_regression.check(str(err.value)) + + +def test_subscription_type_can_be_extended_with_new_fields(): + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + class ExtendChatSubscription(SubscriptionType): + __schema__ = """ + extend type Subscription { + thread: ID! + } + """ + __requires__ = [ChatSubscription] + + +def test_subscription_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on OBJECT" + __visitor__ = SchemaDirectiveVisitor + + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + class ExtendChatSubscription(SubscriptionType): + __schema__ = "extend type Subscription @example" + __requires__ = [ChatSubscription, ExampleDirective] + + +def test_subscription_type_can_be_extended_with_interface(): + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Interface { + threads: ID! + } + """ + + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + class ExtendChatSubscription(SubscriptionType): + __schema__ = """ + extend type Subscription implements Interface { + threads: ID! + } + """ + __requires__ = [ChatSubscription, ExampleInterface] + + +def test_subscription_type_raises_error_when_defined_without_extended_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExtendChatSubscription(SubscriptionType): + __schema__ = """ + extend type Subscription { + thread: ID! + } + """ + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_extended_dependency_is_wrong_type( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleInterface(InterfaceType): + __schema__ = """ + interface Subscription { + id: ID! + } + """ + + class ExtendChatSubscription(SubscriptionType): + __schema__ = """ + extend type Subscription { + thread: ID! + } + """ + __requires__ = [ExampleInterface] + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_alias_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + __aliases__ = { + "userAlerts": "user_alerts", + } + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_resolver_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + @staticmethod + def resolve_group(*_): + return None + + data_regression.check(str(err.value)) + + +def test_subscription_type_raises_error_when_defined_with_sub_for_nonexisting_field( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + @staticmethod + def subscribe_group(*_): + return None + + data_regression.check(str(err.value)) + + +def test_subscription_type_binds_resolver_and_subscriber_to_schema(): + schema = build_schema( + """ + type Query { + hello: String + } + + type Subscription { + chat: ID! + } + """ + ) + + class ChatSubscription(SubscriptionType): + __schema__ = """ + type Subscription { + chat: ID! + } + """ + + @staticmethod + def resolve_chat(*_): + return None + + @staticmethod + def subscribe_chat(*_): + return None + + ChatSubscription.__bind_to_schema__(schema) + + field = schema.type_map.get("Subscription").fields["chat"] + assert field.resolve is ChatSubscription.resolve_chat + assert field.subscribe is ChatSubscription.subscribe_chat diff --git a/tests_v1/test_union_type.py b/tests_v1/test_union_type.py new file mode 100644 index 0000000..61c8a3a --- /dev/null +++ b/tests_v1/test_union_type.py @@ -0,0 +1,251 @@ +from dataclasses import dataclass + +import pytest +from ariadne import SchemaDirectiveVisitor +from graphql import GraphQLError, graphql_sync + +from ariadne_graphql_modules import ( + DirectiveType, + ObjectType, + UnionType, + make_executable_schema, +) + + +def test_union_type_raises_attribute_error_when_defined_without_schema(data_regression): + with pytest.raises(AttributeError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + pass + + data_regression.check(str(err.value)) + + +def test_union_type_raises_error_when_defined_with_invalid_schema_type(data_regression): + with pytest.raises(TypeError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = True + + data_regression.check(str(err.value)) + + +def test_union_type_raises_error_when_defined_with_invalid_schema_str(data_regression): + with pytest.raises(GraphQLError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = "unien Example = A | B" + + data_regression.check(str(err.value)) + + +def test_union_type_raises_error_when_defined_with_invalid_graphql_type_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = "scalar DateTime" + + data_regression.check(str(err.value)) + + +def test_union_type_raises_error_when_defined_with_multiple_types_schema( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = """ + union A = C | D + + union B = C | D + """ + + data_regression.check(str(err.value)) + + +@dataclass +class User: + id: int + name: str + + +@dataclass +class Comment: + id: int + message: str + + +class UserType(ObjectType): + __schema__ = """ + type User { + id: ID! + name: String! + } + """ + + +class CommentType(ObjectType): + __schema__ = """ + type Comment { + id: ID! + message: String! + } + """ + + +class ResultUnion(UnionType): + __schema__ = "union Result = Comment | User" + __requires__ = [CommentType, UserType] + + @staticmethod + def resolve_type(instance, *_): + if isinstance(instance, Comment): + return "Comment" + + if isinstance(instance, User): + return "User" + + return None + + +class QueryType(ObjectType): + __schema__ = """ + type Query { + results: [Result!]! + } + """ + __requires__ = [ResultUnion] + + @staticmethod + def resolve_results(*_): + return [ + User(id=1, name="Alice"), + Comment(id=1, message="Hello world!"), + ] + + +schema = make_executable_schema(QueryType, UserType, CommentType) + + +def test_union_type_extracts_graphql_name(): + class ExampleUnion(UnionType): + __schema__ = "union Example = User | Comment" + __requires__ = [UserType, CommentType] + + assert ExampleUnion.graphql_name == "Example" + + +def test_union_type_raises_error_when_defined_without_member_type_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = "union Example = User | Comment" + __requires__ = [UserType] + + data_regression.check(str(err.value)) + + +def test_interface_type_binds_type_resolver(): + query = """ + query { + results { + ... on User { + __typename + id + name + } + ... on Comment { + __typename + id + message + } + } + } + """ + + result = graphql_sync(schema, query) + assert result.data == { + "results": [ + { + "__typename": "User", + "id": "1", + "name": "Alice", + }, + { + "__typename": "Comment", + "id": "1", + "message": "Hello world!", + }, + ], + } + + +def test_union_type_can_be_extended_with_new_types(): + # pylint: disable=unused-variable + class ExampleUnion(UnionType): + __schema__ = "union Result = User | Comment" + __requires__ = [UserType, CommentType] + + class ThreadType(ObjectType): + __schema__ = """ + type Thread { + id: ID! + title: String! + } + """ + + class ExtendExampleUnion(UnionType): + __schema__ = "union Result = Thread" + __requires__ = [ExampleUnion, ThreadType] + + +def test_union_type_can_be_extended_with_directive(): + # pylint: disable=unused-variable + class ExampleDirective(DirectiveType): + __schema__ = "directive @example on UNION" + __visitor__ = SchemaDirectiveVisitor + + class ExampleUnion(UnionType): + __schema__ = "union Result = User | Comment" + __requires__ = [UserType, CommentType] + + class ExtendExampleUnion(UnionType): + __schema__ = """ + extend union Result @example + """ + __requires__ = [ExampleUnion, ExampleDirective] + + +def test_union_type_raises_error_when_defined_without_extended_dependency( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExtendExampleUnion(UnionType): + __schema__ = "extend union Result = User" + __requires__ = [UserType] + + data_regression.check(str(err.value)) + + +def test_union_type_raises_error_when_extended_dependency_is_wrong_type( + data_regression, +): + with pytest.raises(ValueError) as err: + # pylint: disable=unused-variable + class ExampleType(ObjectType): + __schema__ = """ + type Example { + id: ID! + } + """ + + class ExtendExampleUnion(UnionType): + __schema__ = "extend union Example = User" + __requires__ = [ExampleType, UserType] + + data_regression.check(str(err.value))