graphql-php/tests/Validator/ValidatorTestCase.php

461 lines
16 KiB
PHP
Raw Normal View History

2015-07-15 20:05:46 +03:00
<?php
2018-09-02 14:08:49 +03:00
declare(strict_types=1);
2016-04-09 10:36:53 +03:00
namespace GraphQL\Tests\Validator;
2015-07-15 20:05:46 +03:00
2018-09-26 12:07:23 +03:00
use Exception;
2015-07-15 20:05:46 +03:00
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\Directive;
2015-07-15 20:05:46 +03:00
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
2018-09-02 14:08:49 +03:00
use GraphQL\Type\Schema;
2016-04-09 10:36:53 +03:00
use GraphQL\Validator\DocumentValidator;
2018-07-29 18:43:10 +03:00
use PHPUnit\Framework\TestCase;
2018-09-02 14:08:49 +03:00
use function array_map;
2015-07-15 20:05:46 +03:00
2018-07-29 18:43:10 +03:00
abstract class ValidatorTestCase extends TestCase
2015-07-15 20:05:46 +03:00
{
2018-09-02 14:08:49 +03:00
protected function expectPassesRule($rule, $queryString) : void
{
$this->expectValid(self::getTestSchema(), [$rule], $queryString);
}
protected function expectValid($schema, $rules, $queryString) : void
{
2018-09-19 18:12:09 +03:00
self::assertEquals(
2018-09-02 14:08:49 +03:00
[],
DocumentValidator::validate($schema, Parser::parse($queryString), $rules),
'Should validate'
);
}
2015-07-15 20:05:46 +03:00
/**
* @return Schema
*/
public static function getTestSchema()
2015-07-15 20:05:46 +03:00
{
$FurColor = null;
2015-07-15 20:05:46 +03:00
$Being = new InterfaceType([
2018-09-02 14:08:49 +03:00
'name' => 'Being',
2015-07-15 20:05:46 +03:00
'fields' => [
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2015-07-15 20:05:46 +03:00
],
]);
$Pet = new InterfaceType([
2018-09-02 14:08:49 +03:00
'name' => 'Pet',
2015-07-15 20:05:46 +03:00
'fields' => [
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2015-07-15 20:05:46 +03:00
],
]);
$Canine = new InterfaceType([
2018-09-02 14:08:49 +03:00
'name' => 'Canine',
2018-09-26 12:07:23 +03:00
'fields' => static function () {
return [
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
];
2018-09-02 14:08:49 +03:00
},
]);
2015-07-15 20:05:46 +03:00
$DogCommand = new EnumType([
2018-09-02 14:08:49 +03:00
'name' => 'DogCommand',
2015-07-15 20:05:46 +03:00
'values' => [
2018-09-02 14:08:49 +03:00
'SIT' => ['value' => 0],
2015-07-15 20:05:46 +03:00
'HEEL' => ['value' => 1],
2018-09-02 14:08:49 +03:00
'DOWN' => ['value' => 2],
],
2015-07-15 20:05:46 +03:00
]);
$Dog = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'Dog',
'fields' => [
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2018-09-02 14:08:49 +03:00
'nickname' => ['type' => Type::string()],
'barkVolume' => ['type' => Type::int()],
'barks' => ['type' => Type::boolean()],
2015-07-15 20:05:46 +03:00
'doesKnowCommand' => [
'type' => Type::boolean(),
2018-09-02 14:08:49 +03:00
'args' => ['dogCommand' => ['type' => $DogCommand]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'isHousetrained' => [
2015-07-15 20:05:46 +03:00
'type' => Type::boolean(),
2018-09-02 14:08:49 +03:00
'args' => ['atOtherHomes' => ['type' => Type::boolean(), 'defaultValue' => true]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'isAtLocation' => [
2015-07-15 20:05:46 +03:00
'type' => Type::boolean(),
2018-09-02 14:08:49 +03:00
'args' => ['x' => ['type' => Type::int()], 'y' => ['type' => Type::int()]],
],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'interfaces' => [$Being, $Pet, $Canine],
2015-07-15 20:05:46 +03:00
]);
$Cat = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'Cat',
2018-09-26 12:07:23 +03:00
'fields' => static function () use (&$FurColor) {
return [
2018-09-02 14:08:49 +03:00
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2018-09-02 14:08:49 +03:00
'nickname' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()],
'meowVolume' => ['type' => Type::int()],
2018-09-02 14:08:49 +03:00
'furColor' => $FurColor,
];
},
2018-09-02 14:08:49 +03:00
'interfaces' => [$Being, $Pet],
2015-07-15 20:05:46 +03:00
]);
$CatOrDog = new UnionType([
2018-09-02 14:08:49 +03:00
'name' => 'CatOrDog',
2015-07-15 20:05:46 +03:00
'types' => [$Dog, $Cat],
]);
$Intelligent = new InterfaceType([
2018-09-02 14:08:49 +03:00
'name' => 'Intelligent',
2015-07-15 20:05:46 +03:00
'fields' => [
2018-09-02 14:08:49 +03:00
'iq' => ['type' => Type::int()],
],
2015-07-15 20:05:46 +03:00
]);
2016-04-30 17:39:06 +03:00
$Human = null;
$Human = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'Human',
2015-07-15 20:05:46 +03:00
'interfaces' => [$Being, $Intelligent],
2018-09-26 12:07:23 +03:00
'fields' => static function () use (&$Human, $Pet) {
return [
2018-09-02 14:08:49 +03:00
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2018-09-02 14:08:49 +03:00
'pets' => ['type' => Type::listOf($Pet)],
'relatives' => ['type' => Type::listOf($Human)],
2018-09-02 14:08:49 +03:00
'iq' => ['type' => Type::int()],
];
2018-09-02 14:08:49 +03:00
},
2015-07-15 20:05:46 +03:00
]);
$Alien = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'Alien',
2015-07-15 20:05:46 +03:00
'interfaces' => [$Being, $Intelligent],
2018-09-02 14:08:49 +03:00
'fields' => [
'iq' => ['type' => Type::int()],
'name' => [
'type' => Type::string(),
2018-09-02 14:08:49 +03:00
'args' => ['surname' => ['type' => Type::boolean()]],
],
2018-09-02 14:08:49 +03:00
'numEyes' => ['type' => Type::int()],
],
2015-07-15 20:05:46 +03:00
]);
$DogOrHuman = new UnionType([
2018-09-02 14:08:49 +03:00
'name' => 'DogOrHuman',
2015-07-15 20:05:46 +03:00
'types' => [$Dog, $Human],
]);
$HumanOrAlien = new UnionType([
2018-09-02 14:08:49 +03:00
'name' => 'HumanOrAlien',
2015-07-15 20:05:46 +03:00
'types' => [$Human, $Alien],
]);
$FurColor = new EnumType([
2018-09-02 14:08:49 +03:00
'name' => 'FurColor',
'values' => [
2018-09-02 14:08:49 +03:00
'BROWN' => ['value' => 0],
'BLACK' => ['value' => 1],
'TAN' => ['value' => 2],
'SPOTTED' => ['value' => 3],
2018-09-02 14:08:49 +03:00
'NO_FUR' => ['value' => null],
],
]);
2015-07-15 20:05:46 +03:00
$ComplexInput = new InputObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'ComplexInput',
2015-07-15 20:05:46 +03:00
'fields' => [
2018-09-02 14:08:49 +03:00
'requiredField' => ['type' => Type::nonNull(Type::boolean())],
'intField' => ['type' => Type::int()],
'stringField' => ['type' => Type::string()],
'booleanField' => ['type' => Type::boolean()],
'stringListField' => ['type' => Type::listOf(Type::string())],
],
2015-07-15 20:05:46 +03:00
]);
$ComplicatedArgs = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'ComplicatedArgs',
2015-07-15 20:05:46 +03:00
// TODO List
// TODO Coercion
// TODO NotNulls
'fields' => [
2018-09-02 14:08:49 +03:00
'intArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['intArg' => ['type' => Type::int()]],
],
2018-09-02 14:08:49 +03:00
'nonNullIntArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['nonNullIntArg' => ['type' => Type::nonNull(Type::int())]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'stringArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['stringArg' => ['type' => Type::string()]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'booleanArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['booleanArg' => ['type' => Type::boolean()]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'enumArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['enumArg' => ['type' => $FurColor]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'floatArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['floatArg' => ['type' => Type::float()]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'idArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['idArg' => ['type' => Type::id()]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'stringListArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['stringListArg' => ['type' => Type::listOf(Type::string())]],
],
'stringListNonNullArgField' => [
'type' => Type::string(),
'args' => [
'stringListNonNullArg' => [
'type' => Type::listOf(Type::nonNull(Type::string())),
],
],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'complexArgField' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => ['complexArg' => ['type' => $ComplexInput]],
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'multipleReqs' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => [
'req1' => ['type' => Type::nonNull(Type::int())],
'req2' => ['type' => Type::nonNull(Type::int())],
2015-07-15 20:05:46 +03:00
],
],
2018-09-02 14:08:49 +03:00
'multipleOpts' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => [
'opt1' => [
2018-09-02 14:08:49 +03:00
'type' => Type::int(),
2015-07-15 20:05:46 +03:00
'defaultValue' => 0,
],
'opt2' => [
2018-09-02 14:08:49 +03:00
'type' => Type::int(),
2015-07-15 20:05:46 +03:00
'defaultValue' => 0,
],
],
],
2018-09-02 14:08:49 +03:00
'multipleOptAndReq' => [
2015-07-15 20:05:46 +03:00
'type' => Type::string(),
'args' => [
'req1' => ['type' => Type::nonNull(Type::int())],
'req2' => ['type' => Type::nonNull(Type::int())],
2015-07-15 20:05:46 +03:00
'opt1' => [
2018-09-02 14:08:49 +03:00
'type' => Type::int(),
2015-07-15 20:05:46 +03:00
'defaultValue' => 0,
],
'opt2' => [
2018-09-02 14:08:49 +03:00
'type' => Type::int(),
2015-07-15 20:05:46 +03:00
'defaultValue' => 0,
],
],
],
2018-09-02 14:08:49 +03:00
],
2015-07-15 20:05:46 +03:00
]);
$invalidScalar = new CustomScalarType([
2018-09-02 14:08:49 +03:00
'name' => 'Invalid',
2018-09-26 12:07:23 +03:00
'serialize' => static function ($value) {
return $value;
},
2018-09-26 12:07:23 +03:00
'parseLiteral' => static function ($node) {
throw new Exception('Invalid scalar is always invalid: ' . $node->value);
},
2018-09-26 12:07:23 +03:00
'parseValue' => static function ($node) {
throw new Exception('Invalid scalar is always invalid: ' . $node);
},
]);
$anyScalar = new CustomScalarType([
2018-09-02 14:08:49 +03:00
'name' => 'Any',
2018-09-26 12:07:23 +03:00
'serialize' => static function ($value) {
return $value;
},
2018-09-26 12:07:23 +03:00
'parseLiteral' => static function ($node) {
return $node;
}, // Allows any value
2018-09-26 12:07:23 +03:00
'parseValue' => static function ($value) {
return $value;
}, // Allows any value
]);
2015-07-15 20:05:46 +03:00
$queryRoot = new ObjectType([
2018-09-02 14:08:49 +03:00
'name' => 'QueryRoot',
2015-07-15 20:05:46 +03:00
'fields' => [
2018-09-02 14:08:49 +03:00
'human' => [
2015-07-15 20:05:46 +03:00
'args' => ['id' => ['type' => Type::id()]],
2018-09-02 14:08:49 +03:00
'type' => $Human,
2015-07-15 20:05:46 +03:00
],
2018-09-02 14:08:49 +03:00
'alien' => ['type' => $Alien],
'dog' => ['type' => $Dog],
'cat' => ['type' => $Cat],
'pet' => ['type' => $Pet],
'catOrDog' => ['type' => $CatOrDog],
'dogOrHuman' => ['type' => $DogOrHuman],
'humanOrAlien' => ['type' => $HumanOrAlien],
'complicatedArgs' => ['type' => $ComplicatedArgs],
2018-09-02 14:08:49 +03:00
'invalidArg' => [
'args' => [
2018-09-02 14:08:49 +03:00
'arg' => ['type' => $invalidScalar],
],
'type' => Type::string(),
],
2018-09-02 14:08:49 +03:00
'anyArg' => [
'args' => ['arg' => ['type' => $anyScalar]],
'type' => Type::string(),
],
2018-09-02 14:08:49 +03:00
],
2015-07-15 20:05:46 +03:00
]);
2018-09-26 12:07:23 +03:00
return new Schema([
2018-09-02 14:08:49 +03:00
'query' => $queryRoot,
'directives' => [
Directive::includeDirective(),
Directive::skipDirective(),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onQuery',
'locations' => ['QUERY'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onMutation',
'locations' => ['MUTATION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onSubscription',
'locations' => ['SUBSCRIPTION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onField',
'locations' => ['FIELD'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onFragmentDefinition',
'locations' => ['FRAGMENT_DEFINITION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onFragmentSpread',
'locations' => ['FRAGMENT_SPREAD'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onInlineFragment',
'locations' => ['INLINE_FRAGMENT'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onSchema',
'locations' => ['SCHEMA'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onScalar',
'locations' => ['SCALAR'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onObject',
'locations' => ['OBJECT'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onFieldDefinition',
'locations' => ['FIELD_DEFINITION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onArgumentDefinition',
'locations' => ['ARGUMENT_DEFINITION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onInterface',
'locations' => ['INTERFACE'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onUnion',
'locations' => ['UNION'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onEnum',
'locations' => ['ENUM'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onEnumValue',
'locations' => ['ENUM_VALUE'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onInputObject',
'locations' => ['INPUT_OBJECT'],
]),
new Directive([
2018-09-02 14:08:49 +03:00
'name' => 'onInputFieldDefinition',
'locations' => ['INPUT_FIELD_DEFINITION'],
]),
],
]);
2015-07-15 20:05:46 +03:00
}
2018-09-02 14:08:49 +03:00
protected function expectFailsRule($rule, $queryString, $errors)
2015-07-15 20:05:46 +03:00
{
2018-09-02 14:08:49 +03:00
return $this->expectInvalid(self::getTestSchema(), [$rule], $queryString, $errors);
2015-07-15 20:05:46 +03:00
}
2018-09-02 14:08:49 +03:00
protected function expectInvalid($schema, $rules, $queryString, $expectedErrors)
2015-07-15 20:05:46 +03:00
{
$errors = DocumentValidator::validate($schema, Parser::parse($queryString), $rules);
2015-07-15 20:05:46 +03:00
2018-09-19 18:12:09 +03:00
self::assertNotEmpty($errors, 'GraphQL should not validate');
self::assertEquals($expectedErrors, array_map(['GraphQL\Error\Error', 'formatError'], $errors));
2015-07-15 20:05:46 +03:00
return $errors;
2015-07-15 20:05:46 +03:00
}
2018-09-02 14:08:49 +03:00
protected function expectPassesRuleWithSchema($schema, $rule, $queryString) : void
2015-07-15 20:05:46 +03:00
{
$this->expectValid($schema, [$rule], $queryString);
}
2018-09-02 14:08:49 +03:00
protected function expectFailsRuleWithSchema($schema, $rule, $queryString, $errors) : void
2015-07-15 20:05:46 +03:00
{
$this->expectInvalid($schema, [$rule], $queryString, $errors);
}
2018-09-02 14:08:49 +03:00
protected function expectPassesCompleteValidation($queryString) : void
2015-07-15 20:05:46 +03:00
{
2018-09-02 14:08:49 +03:00
$this->expectValid(self::getTestSchema(), DocumentValidator::allRules(), $queryString);
2015-07-15 20:05:46 +03:00
}
2018-09-02 14:08:49 +03:00
protected function expectFailsCompleteValidation($queryString, $errors) : void
2015-07-15 20:05:46 +03:00
{
2018-09-02 14:08:49 +03:00
$this->expectInvalid(self::getTestSchema(), DocumentValidator::allRules(), $queryString, $errors);
2015-07-15 20:05:46 +03:00
}
}