add depth limit validator tests

This commit is contained in:
Aryan Iyappan 2021-08-14 07:45:34 +05:30
parent a784ef15e5
commit d7b474751d
4 changed files with 297 additions and 12 deletions

View File

@ -20,16 +20,26 @@ Example
Here is how you would implement depth-limiting on your schema. Here is how you would implement depth-limiting on your schema.
.. code:: python .. code:: python
from graphql import validate
from graphene import ObjectType, Schema, String
from graphene.validation import depth_limit_validator from graphene.validation import depth_limit_validator
# The following schema doesn't execute queries
# which have a depth more than 20.
result = schema.execute( class MyQuery(ObjectType):
'THE QUERY', name = String(required=True)
validation_rules=[
schema = Schema(query=MyQuery)
# Queries which have a depth more than 20
# will not be executed.
validation_errors = validate(
schema=schema,
document='THE QUERY',
rules=(
depth_limit_validator( depth_limit_validator(
max_depth=20 max_depth=20
) ),
] )
) )

View File

@ -3,4 +3,4 @@ def is_introspection_key(key):
# > All types and directives defined within a schema must not have a name which # > All types and directives defined within a schema must not have a name which
# > begins with "__" (two underscores), as this is used exclusively # > begins with "__" (two underscores), as this is used exclusively
# > by GraphQLs introspection system. # > by GraphQLs introspection system.
return str(node.name.value).startswith("__") return str(key).startswith("__")

View File

@ -0,0 +1,279 @@
import re
from pytest import raises
from graphql import parse, get_introspection_query, validate
from ...types import Schema, ObjectType, Interface
from ...types import String, Int, List, Field
from ..depth_limit import depth_limit_validator
class PetType(Interface):
name = String(required=True)
class meta:
name = "Pet"
class CatType(ObjectType):
class meta:
name = "Cat"
interfaces = (PetType,)
class DogType(ObjectType):
class meta:
name = "Dog"
interfaces = (PetType,)
class AddressType(ObjectType):
street = String(required=True)
number = Int(required=True)
city = String(required=True)
country = String(required=True)
class Meta:
name = "Address"
class HumanType(ObjectType):
name = String(required=True)
email = String(required=True)
address = Field(AddressType, required=True)
pets = List(PetType, required=True)
class Meta:
name = "Human"
class Query(ObjectType):
user = Field(
HumanType,
required=True,
name=String()
)
version = String(
required=True
)
user1 = Field(
HumanType,
required=True
)
user2 = Field(
HumanType,
required=True
)
user3 = Field(
HumanType,
required=True
)
@staticmethod
def resolve_user(root, info, name=None):
pass
schema = Schema(query=Query)
def run_query(query: str, max_depth: int, ignore=None):
document = parse(query)
result = None
def callback(query_depths):
nonlocal result
result = query_depths
errors = validate(
schema.graphql_schema,
document,
rules=(
depth_limit_validator(
max_depth=max_depth,
ignore=ignore,
callback=callback
),
),
)
return errors, result
def test_should_count_depth_without_fragment():
query = """
query read0 {
version
}
query read1 {
version
user {
name
}
}
query read2 {
matt: user(name: "matt") {
email
}
andy: user(name: "andy") {
email
address {
city
}
}
}
query read3 {
matt: user(name: "matt") {
email
}
andy: user(name: "andy") {
email
address {
city
}
pets {
name
owner {
name
}
}
}
}
"""
expected = {"read0": 0, "read1": 1, "read2": 2, "read3": 3}
errors, result = run_query(query, 10)
assert not errors
assert result == expected
def test_should_count_with_fragments():
query = """
query read0 {
... on Query {
version
}
}
query read1 {
version
user {
... on Human {
name
}
}
}
fragment humanInfo on Human {
email
}
fragment petInfo on Pet {
name
owner {
name
}
}
query read2 {
matt: user(name: "matt") {
...humanInfo
}
andy: user(name: "andy") {
...humanInfo
address {
city
}
}
}
query read3 {
matt: user(name: "matt") {
...humanInfo
}
andy: user(name: "andy") {
... on Human {
email
}
address {
city
}
pets {
...petInfo
}
}
}
"""
expected = {"read0": 0, "read1": 1, "read2": 2, "read3": 3}
errors, result = run_query(query, 10)
assert not errors
assert result == expected
def test_should_ignore_the_introspection_query():
errors, result = run_query(get_introspection_query(), 10)
assert not errors
assert result == {"IntrospectionQuery": 0}
def test_should_catch_very_deep_query():
query = """{
user {
pets {
owner {
pets {
owner {
pets {
name
}
}
}
}
}
}
}
"""
errors, result = run_query(query, 4)
assert len(errors) == 1
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4"
def test_should_ignore_field():
query = """
query read1 {
user { address { city } }
}
query read2 {
user1 { address { city } }
user2 { address { city } }
user3 { address { city } }
}
"""
errors, result = run_query(
query,
10,
ignore=[
"user1",
re.compile("user2"),
lambda field_name: field_name == "user3",
],
)
expected = {"read1": 2, "read2": 0}
assert not errors
assert result == expected
def test_should_raise_invalid_ignore():
query = """
query read1 {
user { address { city } }
}
"""
with raises(ValueError, match="Invalid ignore option:"):
run_query(
query,
10,
ignore=[True],
)

View File

@ -18,10 +18,6 @@ def run_query(query: str):
result = None result = None
def callback(query_depths):
nonlocal result
result = query_depths
errors = validate( errors = validate(
schema.graphql_schema, schema.graphql_schema,
document, document,