1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
from pytest import raises
from graphql.error import GraphQLError
from graphql.language import parse
from graphql.utilities import TypeInfo, build_schema
from graphql.validation import ValidationRule, validate
from .harness import test_schema
def describe_validate_supports_full_validation():
def rejects_invalid_documents():
with raises(TypeError) as exc_info:
# noinspection PyTypeChecker
assert validate(test_schema, None) # type: ignore
assert str(exc_info.value) == "Must provide document."
def rejects_invalid_type_info():
with raises(TypeError) as exc_info:
# noinspection PyTypeChecker
assert validate(
test_schema, parse("query { name }"), type_info={} # type: ignore
)
assert str(exc_info.value) == "Not a TypeInfo object: {}."
def rejects_invalid_rules():
with raises(TypeError) as exc_info:
# noinspection PyTypeChecker
assert validate(
test_schema, parse("query { name }"), rules=[None] # type: ignore
)
assert (
str(exc_info.value) == "Rules must be specified as a collection"
" of ASTValidationRule subclasses."
)
def rejects_invalid_max_errors():
with raises(TypeError) as exc_info:
# noinspection PyTypeChecker
assert validate(
test_schema, parse("query { name }"), max_errors=2.5 # type: ignore
)
assert (
str(exc_info.value)
== "The maximum number of errors must be passed as an int."
)
def validates_queries():
doc = parse(
"""
query {
human {
pets {
... on Cat {
meowsVolume
}
... on Dog {
barkVolume
}
}
}
}
"""
)
errors = validate(test_schema, doc)
assert errors == []
def detects_unknown_fields():
doc = parse(
"""
{
unknown
}
"""
)
errors = validate(test_schema, doc)
assert errors == [
{"message": "Cannot query field 'unknown' on type 'QueryRoot'."}
]
def deprecated_validates_using_a_custom_type_info():
# This TypeInfo will never return a valid field.
type_info = TypeInfo(test_schema, None, lambda *args: None)
doc = parse(
"""
query {
human {
pets {
... on Cat {
meowsVolume
}
... on Dog {
barkVolume
}
}
}
}
"""
)
errors = validate(test_schema, doc, None, None, type_info)
assert [error.message for error in errors] == [
"Cannot query field 'human' on type 'QueryRoot'. Did you mean 'human'?",
"Cannot query field 'meowsVolume' on type 'Cat'."
" Did you mean 'meowsVolume'?",
"Cannot query field 'barkVolume' on type 'Dog'."
" Did you mean 'barkVolume'?",
]
def validates_using_a_custom_rule():
schema = build_schema(
"""
directive @custom(arg: String) on FIELD
type Query {
foo: String
}
"""
)
doc = parse(
"""
query {
name @custom
}
"""
)
class CustomRule(ValidationRule):
def enter_directive(self, node, *_args):
directive_def = self.context.get_directive()
error = GraphQLError("Reporting directive: " + str(directive_def), node)
self.context.report_error(error)
errors = validate(schema, doc, [CustomRule])
assert errors == [
{"message": "Reporting directive: @custom", "locations": [(3, 20)]}
]
def describe_validate_limit_maximum_number_of_validation_errors():
query = """
{
firstUnknownField
secondUnknownField
thirdUnknownField
}
"""
doc = parse(query, no_location=True)
def _validate_document(max_errors=None):
return validate(test_schema, doc, max_errors=max_errors)
def _invalid_field_error(field_name: str):
return {
"message": f"Cannot query field '{field_name}' on type 'QueryRoot'.",
}
def when_max_errors_is_equal_to_number_of_errors():
errors = _validate_document(max_errors=3)
assert errors == [
_invalid_field_error("firstUnknownField"),
_invalid_field_error("secondUnknownField"),
_invalid_field_error("thirdUnknownField"),
]
def when_max_errors_is_less_than_number_of_errors():
errors = _validate_document(max_errors=2)
assert errors == [
_invalid_field_error("firstUnknownField"),
_invalid_field_error("secondUnknownField"),
{
"message": "Too many validation errors, error limit reached."
" Validation aborted."
},
]
def limits_to_100_when_max_errors_is_not_passed():
errors = validate(
test_schema,
parse(
"{" + " ".join(f"unknownField{n}" for n in range(120)) + "}",
no_location=True,
),
)
assert len(errors) == 101
assert errors[0] == _invalid_field_error("unknownField0")
assert errors[-2] == _invalid_field_error("unknownField99")
assert errors[-1] == {
"message": "Too many validation errors, error limit reached."
" Validation aborted."
}
def pass_through_exceptions_from_rules():
class CustomRule(ValidationRule):
def enter_field(self, *_args):
raise RuntimeError("Error from custom rule!")
with raises(RuntimeError, match="^Error from custom rule!$"):
validate(test_schema, doc, [CustomRule], max_errors=1)
|