mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-04-26 20:13:46 +03:00
* treat warnings as errors when running the tests * silence warnings * bugfix: let DjangoConnectionField call its resolver function that is, the one specified using DjangoConnectionField(..., resolver=some_func) * ignore the DeprecationWarning about typing.ByteString in graphql
543 lines
17 KiB
Python
543 lines
17 KiB
Python
from collections import namedtuple
|
|
|
|
import pytest
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
from pytest import raises
|
|
|
|
import graphene
|
|
from graphene import NonNull
|
|
from graphene.relay import ConnectionField, Node
|
|
from graphene.types.datetime import Date, DateTime, Time
|
|
from graphene.types.json import JSONString
|
|
from graphene.types.scalars import BigInt
|
|
|
|
from ..compat import (
|
|
ArrayField,
|
|
HStoreField,
|
|
MissingType,
|
|
RangeField,
|
|
)
|
|
from ..converter import (
|
|
convert_django_field,
|
|
convert_django_field_with_choices,
|
|
generate_enum_name,
|
|
)
|
|
from ..registry import Registry
|
|
from ..types import DjangoObjectType
|
|
from .models import Article, Film, FilmDetails, Reporter, TypedIntChoice, TypedStrChoice
|
|
|
|
# from graphene.core.types.custom_scalars import DateTime, Time, JSONString
|
|
|
|
|
|
def assert_conversion(django_field, graphene_field, *args, **kwargs):
|
|
_kwargs = {**kwargs, "help_text": "Custom Help Text"}
|
|
if "null" not in kwargs:
|
|
_kwargs["null"] = True
|
|
field = django_field(*args, **_kwargs)
|
|
graphene_type = convert_django_field(field)
|
|
assert isinstance(graphene_type, graphene_field)
|
|
field = graphene_type.Field()
|
|
assert field.description == "Custom Help Text"
|
|
|
|
_kwargs = kwargs.copy()
|
|
if "null" not in kwargs:
|
|
_kwargs["null"] = False
|
|
nonnull_field = django_field(*args, **_kwargs)
|
|
if not nonnull_field.null:
|
|
nonnull_graphene_type = convert_django_field(nonnull_field)
|
|
nonnull_field = nonnull_graphene_type.Field()
|
|
assert isinstance(nonnull_field.type, graphene.NonNull)
|
|
return nonnull_field
|
|
return field
|
|
|
|
|
|
def test_should_unknown_django_field_raise_exception():
|
|
with raises(Exception, match="Don't know how to convert the Django field"):
|
|
convert_django_field(None)
|
|
|
|
|
|
def test_should_date_time_convert_string():
|
|
assert_conversion(models.DateTimeField, DateTime)
|
|
|
|
|
|
def test_should_date_convert_string():
|
|
assert_conversion(models.DateField, Date)
|
|
|
|
|
|
def test_should_time_convert_string():
|
|
assert_conversion(models.TimeField, Time)
|
|
|
|
|
|
def test_should_char_convert_string():
|
|
assert_conversion(models.CharField, graphene.String)
|
|
|
|
|
|
def test_should_text_convert_string():
|
|
assert_conversion(models.TextField, graphene.String)
|
|
|
|
|
|
def test_should_email_convert_string():
|
|
assert_conversion(models.EmailField, graphene.String)
|
|
|
|
|
|
def test_should_slug_convert_string():
|
|
assert_conversion(models.SlugField, graphene.String)
|
|
|
|
|
|
def test_should_url_convert_string():
|
|
assert_conversion(models.URLField, graphene.String)
|
|
|
|
|
|
def test_should_ipaddress_convert_string():
|
|
assert_conversion(models.GenericIPAddressField, graphene.String)
|
|
|
|
|
|
def test_should_file_convert_string():
|
|
assert_conversion(models.FileField, graphene.String)
|
|
|
|
|
|
def test_should_image_convert_string():
|
|
assert_conversion(models.ImageField, graphene.String)
|
|
|
|
|
|
def test_should_file_path_field_convert_string():
|
|
assert_conversion(models.FilePathField, graphene.String)
|
|
|
|
|
|
def test_should_auto_convert_id():
|
|
assert_conversion(models.AutoField, graphene.ID, primary_key=True)
|
|
|
|
|
|
def test_should_big_auto_convert_id():
|
|
assert_conversion(models.BigAutoField, graphene.ID, primary_key=True)
|
|
|
|
|
|
def test_should_small_auto_convert_id():
|
|
assert_conversion(models.SmallAutoField, graphene.ID, primary_key=True)
|
|
|
|
|
|
def test_should_uuid_convert_id():
|
|
assert_conversion(models.UUIDField, graphene.UUID)
|
|
|
|
|
|
def test_should_auto_convert_duration():
|
|
assert_conversion(models.DurationField, graphene.Float)
|
|
|
|
|
|
def test_should_positive_integer_convert_int():
|
|
assert_conversion(models.PositiveIntegerField, graphene.Int)
|
|
|
|
|
|
def test_should_positive_small_convert_int():
|
|
assert_conversion(models.PositiveSmallIntegerField, graphene.Int)
|
|
|
|
|
|
def test_should_small_integer_convert_int():
|
|
assert_conversion(models.SmallIntegerField, graphene.Int)
|
|
|
|
|
|
def test_should_big_integer_convert_big_int():
|
|
assert_conversion(models.BigIntegerField, BigInt)
|
|
|
|
|
|
def test_should_integer_convert_int():
|
|
assert_conversion(models.IntegerField, graphene.Int)
|
|
|
|
|
|
def test_should_boolean_convert_boolean():
|
|
assert_conversion(models.BooleanField, graphene.Boolean, null=True)
|
|
|
|
|
|
def test_should_boolean_convert_non_null_boolean():
|
|
field = assert_conversion(models.BooleanField, graphene.Boolean, null=False)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert field.type.of_type == graphene.Boolean
|
|
|
|
|
|
def test_should_nullboolean_convert_boolean():
|
|
assert_conversion(models.NullBooleanField, graphene.Boolean)
|
|
|
|
|
|
def test_field_with_choices_convert_enum():
|
|
field = models.CharField(
|
|
help_text="Language", choices=(("es", "Spanish"), ("en", "English"))
|
|
)
|
|
|
|
class ChoicesModel(models.Model):
|
|
language = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
graphene_type = convert_django_field_with_choices(field).type.of_type
|
|
assert graphene_type._meta.name == "TestChoicesModelLanguageChoices"
|
|
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
|
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
|
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
|
assert graphene_type._meta.enum.__members__["EN"].description == "English"
|
|
|
|
|
|
def test_field_with_callable_choices_convert_enum():
|
|
def get_choices():
|
|
return ("es", "Spanish"), ("en", "English")
|
|
|
|
field = models.CharField(help_text="Language", choices=get_choices)
|
|
|
|
class CallableChoicesModel(models.Model):
|
|
language = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
graphene_type = convert_django_field_with_choices(field).type.of_type
|
|
assert graphene_type._meta.name == "TestCallableChoicesModelLanguageChoices"
|
|
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
|
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
|
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
|
assert graphene_type._meta.enum.__members__["EN"].description == "English"
|
|
|
|
|
|
def test_field_with_grouped_choices():
|
|
field = models.CharField(
|
|
help_text="Language",
|
|
choices=(("Europe", (("es", "Spanish"), ("en", "English"))),),
|
|
)
|
|
|
|
class GroupedChoicesModel(models.Model):
|
|
language = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
convert_django_field_with_choices(field)
|
|
|
|
|
|
def test_field_with_choices_gettext():
|
|
field = models.CharField(
|
|
help_text="Language", choices=(("es", _("Spanish")), ("en", _("English")))
|
|
)
|
|
|
|
class TranslatedChoicesModel(models.Model):
|
|
language = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
convert_django_field_with_choices(field)
|
|
|
|
|
|
def test_field_with_choices_collision():
|
|
field = models.CharField(
|
|
help_text="Timezone",
|
|
choices=(
|
|
("Etc/GMT+1+2", "Fake choice to produce double collision"),
|
|
("Etc/GMT+1", "Greenwich Mean Time +1"),
|
|
("Etc/GMT-1", "Greenwich Mean Time -1"),
|
|
),
|
|
)
|
|
|
|
class CollisionChoicesModel(models.Model):
|
|
timezone = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
convert_django_field_with_choices(field)
|
|
|
|
|
|
def test_field_with_choices_convert_enum_false():
|
|
field = models.CharField(
|
|
help_text="Language", choices=(("es", "Spanish"), ("en", "English"))
|
|
)
|
|
|
|
class TranslatedModel(models.Model):
|
|
language = field
|
|
|
|
class Meta:
|
|
app_label = "test"
|
|
|
|
graphene_type = convert_django_field_with_choices(
|
|
field, convert_choices_to_enum=False
|
|
)
|
|
assert isinstance(graphene_type, graphene.String)
|
|
|
|
|
|
def test_should_float_convert_float():
|
|
assert_conversion(models.FloatField, graphene.Float)
|
|
|
|
|
|
def test_should_float_convert_decimal():
|
|
assert_conversion(models.DecimalField, graphene.Decimal)
|
|
|
|
|
|
def test_should_manytomany_convert_connectionorlist():
|
|
registry = Registry()
|
|
dynamic_field = convert_django_field(Reporter._meta.local_many_to_many[0], registry)
|
|
assert not dynamic_field.get_type()
|
|
|
|
|
|
def test_should_manytomany_convert_connectionorlist_list():
|
|
class A(DjangoObjectType):
|
|
class Meta:
|
|
model = Reporter
|
|
fields = "__all__"
|
|
|
|
graphene_field = convert_django_field(
|
|
Reporter._meta.local_many_to_many[0], A._meta.registry
|
|
)
|
|
assert isinstance(graphene_field, graphene.Dynamic)
|
|
dynamic_field = graphene_field.get_type()
|
|
assert isinstance(dynamic_field, graphene.Field)
|
|
# A NonNull List of NonNull A ([A!]!)
|
|
# https://github.com/graphql-python/graphene-django/issues/448
|
|
assert isinstance(dynamic_field.type, NonNull)
|
|
assert isinstance(dynamic_field.type.of_type, graphene.List)
|
|
assert isinstance(dynamic_field.type.of_type.of_type, NonNull)
|
|
assert dynamic_field.type.of_type.of_type.of_type == A
|
|
|
|
|
|
def test_should_manytomany_convert_connectionorlist_connection():
|
|
class A(DjangoObjectType):
|
|
class Meta:
|
|
model = Reporter
|
|
interfaces = (Node,)
|
|
fields = "__all__"
|
|
|
|
graphene_field = convert_django_field(
|
|
Reporter._meta.local_many_to_many[0], A._meta.registry
|
|
)
|
|
assert isinstance(graphene_field, graphene.Dynamic)
|
|
dynamic_field = graphene_field.get_type()
|
|
assert isinstance(dynamic_field, ConnectionField)
|
|
assert dynamic_field.type.of_type == A._meta.connection
|
|
|
|
|
|
def test_should_manytoone_convert_connectionorlist():
|
|
class A(DjangoObjectType):
|
|
class Meta:
|
|
model = Article
|
|
fields = "__all__"
|
|
|
|
graphene_field = convert_django_field(Reporter.articles.rel, A._meta.registry)
|
|
assert isinstance(graphene_field, graphene.Dynamic)
|
|
dynamic_field = graphene_field.get_type()
|
|
assert isinstance(dynamic_field, graphene.Field)
|
|
# a NonNull List of NonNull A ([A!]!)
|
|
assert isinstance(dynamic_field.type, NonNull)
|
|
assert isinstance(dynamic_field.type.of_type, graphene.List)
|
|
assert isinstance(dynamic_field.type.of_type.of_type, NonNull)
|
|
assert dynamic_field.type.of_type.of_type.of_type == A
|
|
|
|
|
|
def test_should_onetoone_reverse_convert_model():
|
|
class A(DjangoObjectType):
|
|
class Meta:
|
|
model = FilmDetails
|
|
fields = "__all__"
|
|
|
|
graphene_field = convert_django_field(Film.details.related, A._meta.registry)
|
|
assert isinstance(graphene_field, graphene.Dynamic)
|
|
dynamic_field = graphene_field.get_type()
|
|
assert isinstance(dynamic_field, graphene.Field)
|
|
assert dynamic_field.type == A
|
|
|
|
|
|
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
|
|
def test_should_postgres_array_convert_list():
|
|
field = assert_conversion(
|
|
ArrayField, graphene.List, models.CharField(max_length=100)
|
|
)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert isinstance(field.type.of_type, graphene.List)
|
|
assert isinstance(field.type.of_type.of_type, graphene.NonNull)
|
|
assert field.type.of_type.of_type.of_type == graphene.String
|
|
|
|
field = assert_conversion(
|
|
ArrayField, graphene.List, models.CharField(max_length=100, null=True)
|
|
)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert isinstance(field.type.of_type, graphene.List)
|
|
assert field.type.of_type.of_type == graphene.String
|
|
|
|
|
|
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
|
|
def test_should_postgres_array_multiple_convert_list():
|
|
field = assert_conversion(
|
|
ArrayField, graphene.List, ArrayField(models.CharField(max_length=100))
|
|
)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert isinstance(field.type.of_type, graphene.List)
|
|
assert isinstance(field.type.of_type.of_type, graphene.List)
|
|
assert isinstance(field.type.of_type.of_type.of_type, graphene.NonNull)
|
|
assert field.type.of_type.of_type.of_type.of_type == graphene.String
|
|
|
|
field = assert_conversion(
|
|
ArrayField,
|
|
graphene.List,
|
|
ArrayField(models.CharField(max_length=100, null=True)),
|
|
)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert isinstance(field.type.of_type, graphene.List)
|
|
assert isinstance(field.type.of_type.of_type, graphene.List)
|
|
assert field.type.of_type.of_type.of_type == graphene.String
|
|
|
|
|
|
@pytest.mark.skipif(HStoreField is MissingType, reason="HStoreField should exist")
|
|
def test_should_postgres_hstore_convert_string():
|
|
assert_conversion(HStoreField, JSONString)
|
|
|
|
|
|
@pytest.mark.skipif(RangeField is MissingType, reason="RangeField should exist")
|
|
def test_should_postgres_range_convert_list():
|
|
from django.contrib.postgres.fields import IntegerRangeField
|
|
|
|
field = assert_conversion(IntegerRangeField, graphene.List)
|
|
assert isinstance(field.type, graphene.NonNull)
|
|
assert isinstance(field.type.of_type, graphene.List)
|
|
assert isinstance(field.type.of_type.of_type, graphene.NonNull)
|
|
assert field.type.of_type.of_type.of_type == graphene.Int
|
|
|
|
|
|
def test_generate_enum_name():
|
|
MockDjangoModelMeta = namedtuple("DjangoMeta", ["app_label", "object_name"])
|
|
|
|
# Simple case
|
|
field = graphene.Field(graphene.String, name="type")
|
|
model_meta = MockDjangoModelMeta(app_label="users", object_name="User")
|
|
assert generate_enum_name(model_meta, field) == "UsersUserTypeChoices"
|
|
|
|
# More complicated multiple work case
|
|
field = graphene.Field(graphene.String, name="fizz_buzz")
|
|
model_meta = MockDjangoModelMeta(
|
|
app_label="some_long_app_name", object_name="SomeObject"
|
|
)
|
|
assert (
|
|
generate_enum_name(model_meta, field)
|
|
== "SomeLongAppNameSomeObjectFizzBuzzChoices"
|
|
)
|
|
|
|
|
|
def test_generate_v2_enum_name(graphene_settings):
|
|
MockDjangoModelMeta = namedtuple("DjangoMeta", ["app_label", "object_name"])
|
|
graphene_settings.DJANGO_CHOICE_FIELD_ENUM_V2_NAMING = True
|
|
|
|
# Simple case
|
|
field = graphene.Field(graphene.String, name="type")
|
|
model_meta = MockDjangoModelMeta(app_label="users", object_name="User")
|
|
assert generate_enum_name(model_meta, field) == "UserType"
|
|
|
|
# More complicated multiple work case
|
|
field = graphene.Field(graphene.String, name="fizz_buzz")
|
|
model_meta = MockDjangoModelMeta(
|
|
app_label="some_long_app_name", object_name="SomeObject"
|
|
)
|
|
assert generate_enum_name(model_meta, field) == "SomeObjectFizzBuzz"
|
|
|
|
|
|
def test_choice_enum_blank_value():
|
|
"""Test that choice fields with blank values work"""
|
|
|
|
class ReporterType(DjangoObjectType):
|
|
class Meta:
|
|
model = Reporter
|
|
fields = ("callable_choice",)
|
|
|
|
class Query(graphene.ObjectType):
|
|
reporter = graphene.Field(ReporterType)
|
|
|
|
def resolve_reporter(root, info):
|
|
# return a model instance with blank choice field value
|
|
return Reporter(callable_choice="")
|
|
|
|
schema = graphene.Schema(query=Query)
|
|
|
|
result = schema.execute(
|
|
"""
|
|
query {
|
|
reporter {
|
|
callableChoice
|
|
}
|
|
}
|
|
"""
|
|
)
|
|
assert not result.errors
|
|
assert result.data == {
|
|
"reporter": {"callableChoice": None},
|
|
}
|
|
|
|
|
|
def test_typed_choice_value():
|
|
"""Test that typed choices fields are resolved correctly to the enum values"""
|
|
|
|
class ReporterType(DjangoObjectType):
|
|
class Meta:
|
|
model = Reporter
|
|
fields = ("typed_choice", "class_choice", "callable_choice")
|
|
|
|
class Query(graphene.ObjectType):
|
|
reporter = graphene.Field(ReporterType)
|
|
|
|
def resolve_reporter(root, info):
|
|
# assign choice values to the fields instead of their str or int values
|
|
return Reporter(
|
|
typed_choice=TypedIntChoice.CHOICE_THIS,
|
|
class_choice=TypedIntChoice.CHOICE_THAT,
|
|
callable_choice=TypedStrChoice.CHOICE_THIS,
|
|
)
|
|
|
|
class CreateReporter(graphene.Mutation):
|
|
reporter = graphene.Field(ReporterType)
|
|
|
|
def mutate(root, info, **kwargs):
|
|
return CreateReporter(
|
|
reporter=Reporter(
|
|
typed_choice=TypedIntChoice.CHOICE_THIS,
|
|
class_choice=TypedIntChoice.CHOICE_THAT,
|
|
callable_choice=TypedStrChoice.CHOICE_THIS,
|
|
),
|
|
)
|
|
|
|
class Mutation(graphene.ObjectType):
|
|
create_reporter = CreateReporter.Field()
|
|
|
|
schema = graphene.Schema(query=Query, mutation=Mutation)
|
|
|
|
reporter_fragment = """
|
|
fragment reporter on ReporterType {
|
|
typedChoice
|
|
classChoice
|
|
callableChoice
|
|
}
|
|
"""
|
|
|
|
expected_reporter = {
|
|
"typedChoice": "A_1",
|
|
"classChoice": "A_2",
|
|
"callableChoice": "THIS",
|
|
}
|
|
|
|
result = schema.execute(
|
|
reporter_fragment
|
|
+ """
|
|
query {
|
|
reporter { ...reporter }
|
|
}
|
|
"""
|
|
)
|
|
assert not result.errors
|
|
assert result.data["reporter"] == expected_reporter
|
|
|
|
result = schema.execute(
|
|
reporter_fragment
|
|
+ """
|
|
mutation {
|
|
createReporter {
|
|
reporter { ...reporter }
|
|
}
|
|
}
|
|
"""
|
|
)
|
|
assert not result.errors
|
|
assert result.data["createReporter"]["reporter"] == expected_reporter
|