Fix CS in tests/Executor

This commit is contained in:
Simon Podlipsky 2018-09-01 17:07:06 +02:00
parent ec54d6152b
commit b02d25e62c
No known key found for this signature in database
GPG Key ID: 725C2BD962B42663
31 changed files with 3033 additions and 2662 deletions

View File

@ -1,23 +1,27 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Error\UserError; use GraphQL\Error\UserError;
use GraphQL\Error\Warning;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\Cat;
use GraphQL\Tests\Executor\TestClasses\Dog;
use GraphQL\Tests\Executor\TestClasses\Human;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
require_once __DIR__ . '/TestClasses.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/**
* DESCRIBE: Execute: Handles execution of abstract types with promises
*/
class AbstractPromiseTest extends TestCase class AbstractPromiseTest extends TestCase
{ {
// DESCRIBE: Execute: Handles execution of abstract types with promises
/** /**
* @see it('isTypeOf used to resolve runtime type for Interface') * @see it('isTypeOf used to resolve runtime type for Interface')
*/ */
@ -26,8 +30,8 @@ class AbstractPromiseTest extends TestCase
$PetType = new InterfaceType([ $PetType = new InterfaceType([
'name' => 'Pet', 'name' => 'Pet',
'fields' => [ 'fields' => [
'name' => [ 'type' => Type::string() ] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -41,7 +45,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -55,7 +59,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -67,13 +71,13 @@ class AbstractPromiseTest extends TestCase
'resolve' => function () { 'resolve' => function () {
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false) new Cat('Garfield', false),
]; ];
} },
] ],
] ],
]), ]),
'types' => [ $CatType, $DogType ] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -94,9 +98,9 @@ class AbstractPromiseTest extends TestCase
'data' => [ 'data' => [
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
[ 'name' => 'Garfield', 'meows' => false ] ['name' => 'Garfield', 'meows' => false],
] ],
] ],
]; ];
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
@ -107,12 +111,11 @@ class AbstractPromiseTest extends TestCase
*/ */
public function testIsTypeOfCanBeRejected() : void public function testIsTypeOfCanBeRejected() : void
{ {
$PetType = new InterfaceType([ $PetType = new InterfaceType([
'name' => 'Pet', 'name' => 'Pet',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -126,7 +129,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -140,7 +143,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -152,13 +155,13 @@ class AbstractPromiseTest extends TestCase
'resolve' => function () { 'resolve' => function () {
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false) new Cat('Garfield', false),
]; ];
} },
] ],
] ],
]), ]),
'types' => [$CatType, $DogType] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -177,7 +180,7 @@ class AbstractPromiseTest extends TestCase
$expected = [ $expected = [
'data' => [ 'data' => [
'pets' => [null, null] 'pets' => [null, null],
], ],
'errors' => [ 'errors' => [
[ [
@ -188,9 +191,9 @@ class AbstractPromiseTest extends TestCase
[ [
'message' => 'We are testing this error', 'message' => 'We are testing this error',
'locations' => [['line' => 2, 'column' => 7]], 'locations' => [['line' => 2, 'column' => 7]],
'path' => ['pets', 1] 'path' => ['pets', 1],
] ],
] ],
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);
@ -201,7 +204,6 @@ class AbstractPromiseTest extends TestCase
*/ */
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
{ {
$DogType = new ObjectType([ $DogType = new ObjectType([
'name' => 'Dog', 'name' => 'Dog',
'isTypeOf' => function ($obj) { 'isTypeOf' => function ($obj) {
@ -212,7 +214,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -225,12 +227,12 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$PetType = new UnionType([ $PetType = new UnionType([
'name' => 'Pet', 'name' => 'Pet',
'types' => [$DogType, $CatType] 'types' => [$DogType, $CatType],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -241,10 +243,10 @@ class AbstractPromiseTest extends TestCase
'type' => Type::listOf($PetType), 'type' => Type::listOf($PetType),
'resolve' => function () { 'resolve' => function () {
return [new Dog('Odie', true), new Cat('Garfield', false)]; return [new Dog('Odie', true), new Cat('Garfield', false)];
} },
] ],
] ],
]) ]),
]); ]);
$query = '{ $query = '{
@ -266,9 +268,9 @@ class AbstractPromiseTest extends TestCase
'data' => [ 'data' => [
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false] ['name' => 'Garfield', 'meows' => false],
] ],
] ],
]; ];
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
@ -292,19 +294,20 @@ class AbstractPromiseTest extends TestCase
if ($obj instanceof Human) { if ($obj instanceof Human) {
return $HumanType; return $HumanType;
} }
return null; return null;
}); });
}, },
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$HumanType = new ObjectType([ $HumanType = new ObjectType([
'name' => 'Human', 'name' => 'Human',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -313,7 +316,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -322,7 +325,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -336,14 +339,14 @@ class AbstractPromiseTest extends TestCase
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false), new Cat('Garfield', false),
new Human('Jon') new Human('Jon'),
]; ];
}); });
} },
] ],
] ],
]), ]),
'types' => [$CatType, $DogType] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -365,16 +368,16 @@ class AbstractPromiseTest extends TestCase
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false], ['name' => 'Garfield', 'meows' => false],
null null,
] ],
], ],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
'locations' => [['line' => 2, 'column' => 7]], 'locations' => [['line' => 2, 'column' => 7]],
'path' => ['pets', 2] 'path' => ['pets', 2],
],
], ],
]
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);
@ -385,12 +388,11 @@ class AbstractPromiseTest extends TestCase
*/ */
public function testResolveTypeOnUnionYieldsUsefulError() : void public function testResolveTypeOnUnionYieldsUsefulError() : void
{ {
$HumanType = new ObjectType([ $HumanType = new ObjectType([
'name' => 'Human', 'name' => 'Human',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -398,7 +400,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -406,7 +408,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$PetType = new UnionType([ $PetType = new UnionType([
@ -422,10 +424,11 @@ class AbstractPromiseTest extends TestCase
if ($obj instanceof Human) { if ($obj instanceof Human) {
return $HumanType; return $HumanType;
} }
return null; return null;
}); });
}, },
'types' => [$DogType, $CatType] 'types' => [$DogType, $CatType],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -438,12 +441,12 @@ class AbstractPromiseTest extends TestCase
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false), new Cat('Garfield', false),
new Human('Jon') new Human('Jon'),
]; ];
} },
] ],
] ],
]) ]),
]); ]);
$query = '{ $query = '{
@ -466,16 +469,16 @@ class AbstractPromiseTest extends TestCase
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false], ['name' => 'Garfield', 'meows' => false],
null null,
] ],
], ],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
'locations' => [['line' => 2, 'column' => 7]], 'locations' => [['line' => 2, 'column' => 7]],
'path' => ['pets', 2] 'path' => ['pets', 2],
] ],
] ],
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);
@ -496,22 +499,22 @@ class AbstractPromiseTest extends TestCase
if ($obj instanceof Cat) { if ($obj instanceof Cat) {
return 'Cat'; return 'Cat';
} }
return null; return null;
}); });
}, },
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
'name' => 'Dog', 'name' => 'Dog',
'interfaces' => [$PetType], 'interfaces' => [$PetType],
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -520,7 +523,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -532,13 +535,13 @@ class AbstractPromiseTest extends TestCase
'resolve' => function () { 'resolve' => function () {
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false) new Cat('Garfield', false),
]; ];
} },
] ],
] ],
]), ]),
'types' => [$CatType, $DogType] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -560,8 +563,8 @@ class AbstractPromiseTest extends TestCase
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false], ['name' => 'Garfield', 'meows' => false],
] ],
] ],
]; ];
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
@ -571,7 +574,6 @@ class AbstractPromiseTest extends TestCase
*/ */
public function testResolveTypeCanBeCaught() : void public function testResolveTypeCanBeCaught() : void
{ {
$PetType = new InterfaceType([ $PetType = new InterfaceType([
'name' => 'Pet', 'name' => 'Pet',
'resolveType' => function () { 'resolveType' => function () {
@ -580,8 +582,8 @@ class AbstractPromiseTest extends TestCase
}); });
}, },
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -590,7 +592,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -599,7 +601,7 @@ class AbstractPromiseTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -611,13 +613,13 @@ class AbstractPromiseTest extends TestCase
'resolve' => function () { 'resolve' => function () {
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false) new Cat('Garfield', false),
]; ];
} },
] ],
] ],
]), ]),
'types' => [$CatType, $DogType] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -636,20 +638,20 @@ class AbstractPromiseTest extends TestCase
$expected = [ $expected = [
'data' => [ 'data' => [
'pets' => [null, null] 'pets' => [null, null],
], ],
'errors' => [ 'errors' => [
[ [
'message' => 'We are testing this error', 'message' => 'We are testing this error',
'locations' => [['line' => 2, 'column' => 7]], 'locations' => [['line' => 2, 'column' => 7]],
'path' => ['pets', 0] 'path' => ['pets', 0],
], ],
[ [
'message' => 'We are testing this error', 'message' => 'We are testing this error',
'locations' => [['line' => 2, 'column' => 7]], 'locations' => [['line' => 2, 'column' => 7]],
'path' => ['pets', 1] 'path' => ['pets', 1],
] ],
] ],
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);

View File

@ -1,23 +1,28 @@
<?php <?php
namespace GraphQL\Tests\Executor;
require_once __DIR__ . '/TestClasses.php'; declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\Cat;
use GraphQL\Tests\Executor\TestClasses\Dog;
use GraphQL\Tests\Executor\TestClasses\Human;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/**
* Execute: Handles execution of abstract types
*/
class AbstractTest extends TestCase class AbstractTest extends TestCase
{ {
// Execute: Handles execution of abstract types
/** /**
* @see it('isTypeOf used to resolve runtime type for Interface') * @see it('isTypeOf used to resolve runtime type for Interface')
*/ */
@ -27,19 +32,21 @@ class AbstractTest extends TestCase
$petType = new InterfaceType([ $petType = new InterfaceType([
'name' => 'Pet', 'name' => 'Pet',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
// Added to interface type when defined // Added to interface type when defined
$dogType = new ObjectType([ $dogType = new ObjectType([
'name' => 'Dog', 'name' => 'Dog',
'interfaces' => [$petType], 'interfaces' => [$petType],
'isTypeOf' => function($obj) { return $obj instanceof Dog; }, 'isTypeOf' => function ($obj) {
return $obj instanceof Dog;
},
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()] 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$catType = new ObjectType([ $catType = new ObjectType([
@ -51,7 +58,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -62,11 +69,11 @@ class AbstractTest extends TestCase
'type' => Type::listOf($petType), 'type' => Type::listOf($petType),
'resolve' => function () { 'resolve' => function () {
return [new Dog('Odie', true), new Cat('Garfield', false)]; return [new Dog('Odie', true), new Cat('Garfield', false)];
} },
] ],
] ],
]), ]),
'types' => [$catType, $dogType] 'types' => [$catType, $dogType],
]); ]);
$query = '{ $query = '{
@ -84,8 +91,8 @@ class AbstractTest extends TestCase
$expected = new ExecutionResult([ $expected = new ExecutionResult([
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false] ['name' => 'Garfield', 'meows' => false],
] ],
]); ]);
$result = Executor::execute($schema, Parser::parse($query)); $result = Executor::execute($schema, Parser::parse($query));
@ -99,11 +106,13 @@ class AbstractTest extends TestCase
{ {
$dogType = new ObjectType([ $dogType = new ObjectType([
'name' => 'Dog', 'name' => 'Dog',
'isTypeOf' => function($obj) { return $obj instanceof Dog; }, 'isTypeOf' => function ($obj) {
return $obj instanceof Dog;
},
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()] 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$catType = new ObjectType([ $catType = new ObjectType([
@ -114,12 +123,12 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$petType = new UnionType([ $petType = new UnionType([
'name' => 'Pet', 'name' => 'Pet',
'types' => [$dogType, $catType] 'types' => [$dogType, $catType],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -130,10 +139,10 @@ class AbstractTest extends TestCase
'type' => Type::listOf($petType), 'type' => Type::listOf($petType),
'resolve' => function () { 'resolve' => function () {
return [new Dog('Odie', true), new Cat('Garfield', false)]; return [new Dog('Odie', true), new Cat('Garfield', false)];
} },
] ],
] ],
]) ]),
]); ]);
$query = '{ $query = '{
@ -151,8 +160,8 @@ class AbstractTest extends TestCase
$expected = new ExecutionResult([ $expected = new ExecutionResult([
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false] ['name' => 'Garfield', 'meows' => false],
] ],
]); ]);
$this->assertEquals($expected, Executor::execute($schema, Parser::parse($query))); $this->assertEquals($expected, Executor::execute($schema, Parser::parse($query)));
@ -161,7 +170,7 @@ class AbstractTest extends TestCase
/** /**
* @see it('resolveType on Interface yields useful error') * @see it('resolveType on Interface yields useful error')
*/ */
function testResolveTypeOnInterfaceYieldsUsefulError() public function testResolveTypeOnInterfaceYieldsUsefulError() : void
{ {
$DogType = null; $DogType = null;
$CatType = null; $CatType = null;
@ -179,18 +188,19 @@ class AbstractTest extends TestCase
if ($obj instanceof Human) { if ($obj instanceof Human) {
return $HumanType; return $HumanType;
} }
return null; return null;
}, },
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$HumanType = new ObjectType([ $HumanType = new ObjectType([
'name' => 'Human', 'name' => 'Human',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -199,7 +209,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -208,7 +218,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -221,16 +231,15 @@ class AbstractTest extends TestCase
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false), new Cat('Garfield', false),
new Human('Jon') new Human('Jon'),
]; ];
} },
] ],
], ],
]), ]),
'types' => [$DogType, $CatType] 'types' => [$DogType, $CatType],
]); ]);
$query = '{ $query = '{
pets { pets {
name name
@ -248,14 +257,15 @@ class AbstractTest extends TestCase
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false], ['name' => 'Garfield', 'meows' => false],
null null,
] ],
], ],
'errors' => [[ 'errors' => [[
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
'locations' => [['line' => 2, 'column' => 11]], 'locations' => [['line' => 2, 'column' => 11]],
'path' => ['pets', 2] 'path' => ['pets', 2],
]] ],
],
]; ];
$actual = GraphQL::executeQuery($schema, $query)->toArray(true); $actual = GraphQL::executeQuery($schema, $query)->toArray(true);
@ -271,7 +281,7 @@ class AbstractTest extends TestCase
'name' => 'Human', 'name' => 'Human',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -279,7 +289,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -287,7 +297,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$PetType = new UnionType([ $PetType = new UnionType([
@ -303,7 +313,7 @@ class AbstractTest extends TestCase
return $HumanType; return $HumanType;
} }
}, },
'types' => [$DogType, $CatType] 'types' => [$DogType, $CatType],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -316,12 +326,12 @@ class AbstractTest extends TestCase
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false), new Cat('Garfield', false),
new Human('Jon') new Human('Jon'),
]; ];
} },
] ],
] ],
]) ]),
]); ]);
$query = '{ $query = '{
@ -341,18 +351,23 @@ class AbstractTest extends TestCase
$expected = [ $expected = [
'data' => [ 'data' => [
'pets' => [ 'pets' => [
['name' => 'Odie', [
'woofs' => true], 'name' => 'Odie',
['name' => 'Garfield', 'woofs' => true,
'meows' => false], ],
null [
] 'name' => 'Garfield',
'meows' => false,
],
null,
],
], ],
'errors' => [[ 'errors' => [[
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
'locations' => [['line' => 2, 'column' => 11]], 'locations' => [['line' => 2, 'column' => 11]],
'path' => ['pets', 2] 'path' => ['pets', 2],
]] ],
],
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);
} }
@ -420,13 +435,18 @@ class AbstractTest extends TestCase
$PetType = new InterfaceType([ $PetType = new InterfaceType([
'name' => 'Pet', 'name' => 'Pet',
'resolveType' => function ($obj) { 'resolveType' => function ($obj) {
if ($obj instanceof Dog) return 'Dog'; if ($obj instanceof Dog) {
if ($obj instanceof Cat) return 'Cat'; return 'Dog';
}
if ($obj instanceof Cat) {
return 'Cat';
}
return null; return null;
}, },
'fields' => [ 'fields' => [
'name' => [ 'type' => Type::string() ] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -435,7 +455,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()], 'woofs' => ['type' => Type::boolean()],
] ],
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -444,7 +464,7 @@ class AbstractTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -456,13 +476,13 @@ class AbstractTest extends TestCase
'resolve' => function () { 'resolve' => function () {
return [ return [
new Dog('Odie', true), new Dog('Odie', true),
new Cat('Garfield', false) new Cat('Garfield', false),
]; ];
} },
] ],
] ],
]), ]),
'types' => [ $CatType, $DogType ] 'types' => [$CatType, $DogType],
]); ]);
$query = '{ $query = '{
@ -479,14 +499,17 @@ class AbstractTest extends TestCase
$result = GraphQL::executeQuery($schema, $query)->toArray(); $result = GraphQL::executeQuery($schema, $query)->toArray();
$this->assertEquals([ $this->assertEquals(
[
'data' => [ 'data' => [
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false] ['name' => 'Garfield', 'meows' => false],
] ],
] ],
], $result); ],
$result
);
} }
public function testHintsOnConflictingTypeInstancesInResolveType() : void public function testHintsOnConflictingTypeInstancesInResolveType() : void
@ -495,35 +518,33 @@ class AbstractTest extends TestCase
return new ObjectType([ return new ObjectType([
'name' => 'Test', 'name' => 'Test',
'fields' => [ 'fields' => [
'a' => Type::string() 'a' => Type::string(),
], ],
'interfaces' => function () use ($iface) { 'interfaces' => function () use ($iface) {
return [$iface]; return [$iface];
} },
]); ]);
}; };
$iface = new InterfaceType([ $iface = new InterfaceType([
'name' => 'Node', 'name' => 'Node',
'fields' => [ 'fields' => [
'a' => Type::string() 'a' => Type::string(),
], ],
'resolveType' => function () use (&$createTest) { 'resolveType' => function () use (&$createTest) {
return $createTest(); return $createTest();
} },
]); ]);
$query = new ObjectType([ $query = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'node' => $iface, 'node' => $iface,
'test' => $createTest() 'test' => $createTest(),
] ],
]); ]);
$schema = new Schema([ $schema = new Schema(['query' => $query]);
'query' => $query,
]);
$schema->assertValid(); $schema->assertValid();
$query = ' $query = '

View File

@ -1,33 +1,44 @@
<?php <?php
namespace GraphQL\Tests\Executor;
declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function in_array;
class DeferredFieldsTest extends TestCase class DeferredFieldsTest extends TestCase
{ {
/** @var ObjectType */
private $userType; private $userType;
/** @var ObjectType */
private $storyType; private $storyType;
/** @var ObjectType */
private $categoryType; private $categoryType;
/** @var */
private $path; private $path;
/** @var mixed[][] */
private $storyDataSource; private $storyDataSource;
/** @var mixed[][] */
private $userDataSource; private $userDataSource;
/** @var mixed[][] */
private $categoryDataSource; private $categoryDataSource;
/** @var ObjectType */
private $queryType; private $queryType;
public function setUp() public function setUp()
@ -54,7 +65,7 @@ class DeferredFieldsTest extends TestCase
$this->categoryDataSource = [ $this->categoryDataSource = [
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8], ['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3], ['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3],
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9] ['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9],
]; ];
$this->path = []; $this->path = [];
@ -66,8 +77,9 @@ class DeferredFieldsTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($user, $args, $context, ResolveInfo $info) { 'resolve' => function ($user, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return $user['name']; return $user['name'];
} },
], ],
'bestFriend' => [ 'bestFriend' => [
'type' => $this->userType, 'type' => $this->userType,
@ -76,14 +88,18 @@ class DeferredFieldsTest extends TestCase
return new Deferred(function () use ($user) { return new Deferred(function () use ($user) {
$this->path[] = 'deferred-for-best-friend-of-' . $user['id']; $this->path[] = 'deferred-for-best-friend-of-' . $user['id'];
return Utils::find($this->userDataSource, function($entry) use ($user) {
return Utils::find(
$this->userDataSource,
function ($entry) use ($user) {
return $entry['id'] === $user['bestFriendId']; return $entry['id'] === $user['bestFriendId'];
});
});
} }
] );
});
},
],
]; ];
} },
]); ]);
$this->storyType = new ObjectType([ $this->storyType = new ObjectType([
@ -93,8 +109,9 @@ class DeferredFieldsTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($entry, $args, $context, ResolveInfo $info) { 'resolve' => function ($entry, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return $entry['title']; return $entry['title'];
} },
], ],
'author' => [ 'author' => [
'type' => $this->userType, 'type' => $this->userType,
@ -103,13 +120,17 @@ class DeferredFieldsTest extends TestCase
return new Deferred(function () use ($story) { return new Deferred(function () use ($story) {
$this->path[] = 'deferred-for-story-' . $story['id'] . '-author'; $this->path[] = 'deferred-for-story-' . $story['id'] . '-author';
return Utils::find($this->userDataSource, function($entry) use ($story) {
return Utils::find(
$this->userDataSource,
function ($entry) use ($story) {
return $entry['id'] === $story['authorId']; return $entry['id'] === $story['authorId'];
});
});
} }
] );
] });
},
],
],
]); ]);
$this->categoryType = new ObjectType([ $this->categoryType = new ObjectType([
@ -119,18 +140,23 @@ class DeferredFieldsTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($category, $args, $context, ResolveInfo $info) { 'resolve' => function ($category, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return $category['name']; return $category['name'];
} },
], ],
'stories' => [ 'stories' => [
'type' => Type::listOf($this->storyType), 'type' => Type::listOf($this->storyType),
'resolve' => function ($category, $args, $context, ResolveInfo $info) { 'resolve' => function ($category, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return Utils::filter($this->storyDataSource, function($story) use ($category) {
return Utils::filter(
$this->storyDataSource,
function ($story) use ($category) {
return in_array($category['id'], $story['categoryIds']); return in_array($category['id'], $story['categoryIds']);
});
} }
);
},
], ],
'topStory' => [ 'topStory' => [
'type' => $this->storyType, 'type' => $this->storyType,
@ -139,13 +165,17 @@ class DeferredFieldsTest extends TestCase
return new Deferred(function () use ($category) { return new Deferred(function () use ($category) {
$this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory'; $this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory';
return Utils::find($this->storyDataSource, function($story) use ($category) {
return Utils::find(
$this->storyDataSource,
function ($story) use ($category) {
return $story['id'] === $category['topStoryId']; return $story['id'] === $category['topStoryId'];
});
});
} }
] );
] });
},
],
],
]); ]);
$this->queryType = new ObjectType([ $this->queryType = new ObjectType([
@ -155,26 +185,32 @@ class DeferredFieldsTest extends TestCase
'type' => Type::listOf($this->storyType), 'type' => Type::listOf($this->storyType),
'resolve' => function ($val, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return Utils::filter($this->storyDataSource, function($story) {
return Utils::filter(
$this->storyDataSource,
function ($story) {
return $story['id'] % 2 === 1; return $story['id'] % 2 === 1;
});
} }
);
},
], ],
'featuredCategory' => [ 'featuredCategory' => [
'type' => $this->categoryType, 'type' => $this->categoryType,
'resolve' => function ($val, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return $this->categoryDataSource[0]; return $this->categoryDataSource[0];
} },
], ],
'categories' => [ 'categories' => [
'type' => Type::listOf($this->categoryType), 'type' => Type::listOf($this->categoryType),
'resolve' => function ($val, $args, $context, ResolveInfo $info) { 'resolve' => function ($val, $args, $context, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return $this->categoryDataSource; return $this->categoryDataSource;
} },
] ],
] ],
]); ]);
parent::setUp(); parent::setUp();
@ -202,7 +238,7 @@ class DeferredFieldsTest extends TestCase
'); ');
$schema = new Schema([ $schema = new Schema([
'query' => $this->queryType 'query' => $this->queryType,
]); ]);
$expected = [ $expected = [
@ -220,9 +256,9 @@ class DeferredFieldsTest extends TestCase
['title' => 'Story #4', 'author' => ['name' => 'Joe']], ['title' => 'Story #4', 'author' => ['name' => 'Joe']],
['title' => 'Story #6', 'author' => ['name' => 'Jane']], ['title' => 'Story #6', 'author' => ['name' => 'Jane']],
['title' => 'Story #8', 'author' => ['name' => 'John']], ['title' => 'Story #8', 'author' => ['name' => 'John']],
] ],
] ],
] ],
]; ];
$result = Executor::execute($schema, $query); $result = Executor::execute($schema, $query);
@ -292,7 +328,7 @@ class DeferredFieldsTest extends TestCase
'); ');
$schema = new Schema([ $schema = new Schema([
'query' => $this->queryType 'query' => $this->queryType,
]); ]);
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']]; $author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
@ -306,8 +342,8 @@ class DeferredFieldsTest extends TestCase
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]], ['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]], ['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]], ['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
] ],
] ],
]; ];
$result = Executor::execute($schema, $query); $result = Executor::execute($schema, $query);
@ -359,8 +395,9 @@ class DeferredFieldsTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($v, $a, $c, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return 'sync'; return 'sync';
} },
], ],
'deferred' => [ 'deferred' => [
'type' => Type::string(), 'type' => Type::string(),
@ -369,16 +406,18 @@ class DeferredFieldsTest extends TestCase
return new Deferred(function () use ($info) { return new Deferred(function () use ($info) {
$this->path[] = ['!dfd for: ', $info->path]; $this->path[] = ['!dfd for: ', $info->path];
return 'deferred'; return 'deferred';
}); });
} },
], ],
'nest' => [ 'nest' => [
'type' => $complexType, 'type' => $complexType,
'resolve' => function ($v, $a, $c, ResolveInfo $info) { 'resolve' => function ($v, $a, $c, ResolveInfo $info) {
$this->path[] = $info->path; $this->path[] = $info->path;
return []; return [];
} },
], ],
'deferredNest' => [ 'deferredNest' => [
'type' => $complexType, 'type' => $complexType,
@ -387,17 +426,16 @@ class DeferredFieldsTest extends TestCase
return new Deferred(function () use ($info) { return new Deferred(function () use ($info) {
$this->path[] = ['!dfd nest for: ', $info->path]; $this->path[] = ['!dfd nest for: ', $info->path];
return []; return [];
}); });
} },
] ],
]; ];
} },
]); ]);
$schema = new Schema([ $schema = new Schema(['query' => $complexType]);
'query' => $complexType
]);
$query = Parser::parse(' $query = Parser::parse('
{ {
@ -435,26 +473,26 @@ class DeferredFieldsTest extends TestCase
'deferred' => 'deferred', 'deferred' => 'deferred',
'nest' => [ 'nest' => [
'sync' => 'sync', 'sync' => 'sync',
'deferred' => 'deferred' 'deferred' => 'deferred',
], ],
'deferredNest' => [ 'deferredNest' => [
'sync' => 'sync', 'sync' => 'sync',
'deferred' => 'deferred' 'deferred' => 'deferred',
] ],
], ],
'deferredNest' => [ 'deferredNest' => [
'sync' => 'sync', 'sync' => 'sync',
'deferred' => 'deferred', 'deferred' => 'deferred',
'nest' => [ 'nest' => [
'sync' => 'sync', 'sync' => 'sync',
'deferred' => 'deferred' 'deferred' => 'deferred',
], ],
'deferredNest' => [ 'deferredNest' => [
'sync' => 'sync', 'sync' => 'sync',
'deferred' => 'deferred' 'deferred' => 'deferred',
] ],
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());

View File

@ -1,16 +1,27 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema; use GraphQL\Language\Source;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
/**
* Describe: Execute: handles directives
*/
class DirectivesTest extends TestCase class DirectivesTest extends TestCase
{ {
// Describe: Execute: handles directives /** @var Schema */
private static $schema;
/** @var string[] */
private static $data;
/** /**
* @see it('basic query works') * @see it('basic query works')
@ -20,10 +31,50 @@ class DirectivesTest extends TestCase
$this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b }')); $this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b }'));
} }
/**
* @param Source|string $doc
* @return mixed[]
*/
private function executeTestQuery($doc) : array
{
return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray();
}
private static function getSchema() : Schema
{
if (! self::$schema) {
self::$schema = new Schema([
'query' => new ObjectType([
'name' => 'TestType',
'fields' => [
'a' => ['type' => Type::string()],
'b' => ['type' => Type::string()],
],
]),
]);
}
return self::$schema;
}
/**
* @return string[]
*/
private static function getData() : array
{
return self::$data ?: (self::$data = [
'a' => 'a',
'b' => 'b',
]);
}
public function testWorksOnScalars() : void public function testWorksOnScalars() : void
{ {
// if true includes scalar // if true includes scalar
$this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b @include(if: true) }')); $this->assertEquals(
['data' => ['a' => 'a', 'b' => 'b']],
$this->executeTestQuery('{ a, b @include(if: true) }')
);
// if false omits on scalar // if false omits on scalar
$this->assertEquals(['data' => ['a' => 'a']], $this->executeTestQuery('{ a, b @include(if: false) }')); $this->assertEquals(['data' => ['a' => 'a']], $this->executeTestQuery('{ a, b @include(if: false) }'));
@ -200,37 +251,4 @@ class DirectivesTest extends TestCase
$this->executeTestQuery('{ a, b @include(if: false) @skip(if: false) }') $this->executeTestQuery('{ a, b @include(if: false) @skip(if: false) }')
); );
} }
private static $schema;
private static $data;
private static function getSchema()
{
if (!self::$schema) {
self::$schema = new Schema([
'query' => new ObjectType([
'name' => 'TestType',
'fields' => [
'a' => ['type' => Type::string()],
'b' => ['type' => Type::string()]
]
])
]);
}
return self::$schema;
}
private static function getData()
{
return self::$data ?: (self::$data = [
'a' => 'a',
'b' => 'b'
]);
}
private function executeTestQuery($doc)
{
return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray();
}
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\ExecutionResult;

View File

@ -1,44 +1,62 @@
<?php <?php
namespace GraphQL\Tests\Executor;
require_once __DIR__ . '/TestClasses.php'; declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Error\Warning; use GraphQL\Error\Warning;
use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Tests\Executor\TestClasses\Cat;
use GraphQL\Tests\Executor\TestClasses\Dog;
use GraphQL\Type\Definition\CustomScalarType; use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema; use GraphQL\Type\Schema;
use PHPUnit\Framework\Error\Error; use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function count;
class ExecutorLazySchemaTest extends TestCase class ExecutorLazySchemaTest extends TestCase
{ {
public $SomeScalarType; /** @var ScalarType */
public $someScalarType;
public $SomeObjectType; /** @var ObjectType */
public $someObjectType;
public $OtherObjectType; /** @var ObjectType */
public $otherObjectType;
public $DeeperObjectType; /** @var ObjectType */
public $deeperObjectType;
public $SomeUnionType; /** @var UnionType */
public $someUnionType;
public $SomeInterfaceType; /** @var InterfaceType */
public $someInterfaceType;
public $SomeEnumType; /** @var EnumType */
public $someEnumType;
public $SomeInputObjectType; /** @var InputObjectType */
public $someInputObjectType;
public $QueryType; /** @var ObjectType */
public $queryType;
/** @var string[] */
public $calls = []; public $calls = [];
/** @var bool[] */
public $loadedTypes = []; public $loadedTypes = [];
public function testWarnsAboutSlowIsTypeOfForLazySchema() : void public function testWarnsAboutSlowIsTypeOfForLazySchema() : void
@ -48,22 +66,24 @@ class ExecutorLazySchemaTest extends TestCase
'name' => 'Pet', 'name' => 'Pet',
'fields' => function () { 'fields' => function () {
return [ return [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
]; ];
} },
]); ]);
// Added to interface type when defined // Added to interface type when defined
$dogType = new ObjectType([ $dogType = new ObjectType([
'name' => 'Dog', 'name' => 'Dog',
'interfaces' => [$petType], 'interfaces' => [$petType],
'isTypeOf' => function($obj) { return $obj instanceof Dog; }, 'isTypeOf' => function ($obj) {
return $obj instanceof Dog;
},
'fields' => function () { 'fields' => function () {
return [ return [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()] 'woofs' => ['type' => Type::boolean()],
]; ];
} },
]); ]);
$catType = new ObjectType([ $catType = new ObjectType([
@ -77,7 +97,7 @@ class ExecutorLazySchemaTest extends TestCase
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()], 'meows' => ['type' => Type::boolean()],
]; ];
} },
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -88,9 +108,9 @@ class ExecutorLazySchemaTest extends TestCase
'type' => Type::listOf($petType), 'type' => Type::listOf($petType),
'resolve' => function () { 'resolve' => function () {
return [new Dog('Odie', true), new Cat('Garfield', false)]; return [new Dog('Odie', true), new Cat('Garfield', false)];
} },
] ],
] ],
]), ]),
'types' => [$catType, $dogType], 'types' => [$catType, $dogType],
'typeLoader' => function ($name) use ($dogType, $petType, $catType) { 'typeLoader' => function ($name) use ($dogType, $petType, $catType) {
@ -102,7 +122,7 @@ class ExecutorLazySchemaTest extends TestCase
case 'Cat': case 'Cat':
return $catType; return $catType;
} }
} },
]); ]);
$query = '{ $query = '{
@ -120,7 +140,7 @@ class ExecutorLazySchemaTest extends TestCase
$expected = new ExecutionResult([ $expected = new ExecutionResult([
'pets' => [ 'pets' => [
['name' => 'Odie', 'woofs' => true], ['name' => 'Odie', 'woofs' => true],
['name' => 'Garfield', 'meows' => false] ['name' => 'Garfield', 'meows' => false],
], ],
]); ]);
@ -135,10 +155,11 @@ class ExecutorLazySchemaTest extends TestCase
$this->assertEquals( $this->assertEquals(
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of ' . 'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of ' .
'GraphQL\Tests\Executor\Dog. Switching to slow resolution method using `isTypeOf` of all possible '. 'GraphQL\Tests\Executor\TestClasses\Dog. Switching to slow resolution method using `isTypeOf` of all possible ' .
'implementations. It requires full schema scan and degrades query performance significantly. ' . 'implementations. It requires full schema scan and degrades query performance significantly. ' .
'Make sure your `resolveType` always returns valid implementation or throws.', 'Make sure your `resolveType` always returns valid implementation or throws.',
$result->errors[0]->getMessage()); $result->errors[0]->getMessage()
);
} }
public function testHintsOnConflictingTypeInstancesInDefinitions() : void public function testHintsOnConflictingTypeInstancesInDefinitions() : void
@ -154,23 +175,25 @@ class ExecutorLazySchemaTest extends TestCase
return [ return [
'test' => Type::string(), 'test' => Type::string(),
]; ];
} },
]); ]);
default: default:
return null; return null;
} }
}; };
$query = new ObjectType([ $query = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => function () use ($typeLoader) { 'fields' => function () use ($typeLoader) {
return [ return [
'test' => $typeLoader('Test') 'test' => $typeLoader('Test'),
]; ];
} },
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => $query, 'query' => $query,
'typeLoader' => $typeLoader 'typeLoader' => $typeLoader,
]); ]);
$query = ' $query = '
@ -203,7 +226,7 @@ class ExecutorLazySchemaTest extends TestCase
'query' => $this->loadType('Query'), 'query' => $this->loadType('Query'),
'typeLoader' => function ($name) { 'typeLoader' => function ($name) {
return $this->loadType($name, true); return $this->loadType($name, true);
} },
]); ]);
$query = '{ object { string } }'; $query = '{ object { string } }';
@ -219,19 +242,126 @@ class ExecutorLazySchemaTest extends TestCase
$expectedExecutorCalls = [ $expectedExecutorCalls = [
'Query.fields', 'Query.fields',
'SomeObject', 'SomeObject',
'SomeObject.fields' 'SomeObject.fields',
]; ];
$this->assertEquals($expected, $result->toArray(true)); $this->assertEquals($expected, $result->toArray(true));
$this->assertEquals($expectedExecutorCalls, $this->calls); $this->assertEquals($expectedExecutorCalls, $this->calls);
} }
public function loadType($name, $isExecutorCall = false)
{
if ($isExecutorCall) {
$this->calls[] = $name;
}
$this->loadedTypes[$name] = true;
switch ($name) {
case 'Query':
return $this->queryType ?: $this->queryType = new ObjectType([
'name' => 'Query',
'fields' => function () {
$this->calls[] = 'Query.fields';
return [
'object' => ['type' => $this->loadType('SomeObject')],
'other' => ['type' => $this->loadType('OtherObject')],
];
},
]);
case 'SomeObject':
return $this->someObjectType ?: $this->someObjectType = new ObjectType([
'name' => 'SomeObject',
'fields' => function () {
$this->calls[] = 'SomeObject.fields';
return [
'string' => ['type' => Type::string()],
'object' => ['type' => $this->someObjectType],
];
},
'interfaces' => function () {
$this->calls[] = 'SomeObject.interfaces';
return [
$this->loadType('SomeInterface'),
];
},
]);
case 'OtherObject':
return $this->otherObjectType ?: $this->otherObjectType = new ObjectType([
'name' => 'OtherObject',
'fields' => function () {
$this->calls[] = 'OtherObject.fields';
return [
'union' => ['type' => $this->loadType('SomeUnion')],
'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))],
];
},
]);
case 'DeeperObject':
return $this->deeperObjectType ?: $this->deeperObjectType = new ObjectType([
'name' => 'DeeperObject',
'fields' => function () {
return [
'scalar' => ['type' => $this->loadType('SomeScalar')],
];
},
]);
case 'SomeScalar':
return $this->someScalarType ?: $this->someScalarType = new CustomScalarType([
'name' => 'SomeScalar',
'serialize' => function ($value) {
return $value;
},
'parseValue' => function ($value) {
return $value;
},
'parseLiteral' => function () {
},
]);
case 'SomeUnion':
return $this->someUnionType ?: $this->someUnionType = new UnionType([
'name' => 'SomeUnion',
'resolveType' => function () {
$this->calls[] = 'SomeUnion.resolveType';
return $this->loadType('DeeperObject');
},
'types' => function () {
$this->calls[] = 'SomeUnion.types';
return [$this->loadType('DeeperObject')];
},
]);
case 'SomeInterface':
return $this->someInterfaceType ?: $this->someInterfaceType = new InterfaceType([
'name' => 'SomeInterface',
'resolveType' => function () {
$this->calls[] = 'SomeInterface.resolveType';
return $this->loadType('SomeObject');
},
'fields' => function () {
$this->calls[] = 'SomeInterface.fields';
return [
'string' => ['type' => Type::string()],
];
},
]);
default:
return null;
}
}
public function testDeepQuery() : void public function testDeepQuery() : void
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->loadType('Query'), 'query' => $this->loadType('Query'),
'typeLoader' => function ($name) { 'typeLoader' => function ($name) {
return $this->loadType($name, true); return $this->loadType($name, true);
} },
]); ]);
$query = '{ object { object { object { string } } } }'; $query = '{ object { object { object { string } } } }';
@ -242,12 +372,12 @@ class ExecutorLazySchemaTest extends TestCase
); );
$expected = [ $expected = [
'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]] 'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]],
]; ];
$expectedLoadedTypes = [ $expectedLoadedTypes = [
'Query' => true, 'Query' => true,
'SomeObject' => true, 'SomeObject' => true,
'OtherObject' => true 'OtherObject' => true,
]; ];
$this->assertEquals($expected, $result->toArray(true)); $this->assertEquals($expected, $result->toArray(true));
@ -256,7 +386,7 @@ class ExecutorLazySchemaTest extends TestCase
$expectedExecutorCalls = [ $expectedExecutorCalls = [
'Query.fields', 'Query.fields',
'SomeObject', 'SomeObject',
'SomeObject.fields' 'SomeObject.fields',
]; ];
$this->assertEquals($expectedExecutorCalls, $this->calls); $this->assertEquals($expectedExecutorCalls, $this->calls);
} }
@ -267,7 +397,7 @@ class ExecutorLazySchemaTest extends TestCase
'query' => $this->loadType('Query'), 'query' => $this->loadType('Query'),
'typeLoader' => function ($name) { 'typeLoader' => function ($name) {
return $this->loadType($name, true); return $this->loadType($name, true);
} },
]); ]);
$query = ' $query = '
@ -313,98 +443,4 @@ class ExecutorLazySchemaTest extends TestCase
]; ];
$this->assertEquals($expectedCalls, $this->calls); $this->assertEquals($expectedCalls, $this->calls);
} }
public function loadType($name, $isExecutorCall = false)
{
if ($isExecutorCall) {
$this->calls[] = $name;
}
$this->loadedTypes[$name] = true;
switch ($name) {
case 'Query':
return $this->QueryType ?: $this->QueryType = new ObjectType([
'name' => 'Query',
'fields' => function() {
$this->calls[] = 'Query.fields';
return [
'object' => ['type' => $this->loadType('SomeObject')],
'other' => ['type' => $this->loadType('OtherObject')],
];
}
]);
case 'SomeObject':
return $this->SomeObjectType ?: $this->SomeObjectType = new ObjectType([
'name' => 'SomeObject',
'fields' => function() {
$this->calls[] = 'SomeObject.fields';
return [
'string' => ['type' => Type::string()],
'object' => ['type' => $this->SomeObjectType]
];
},
'interfaces' => function() {
$this->calls[] = 'SomeObject.interfaces';
return [
$this->loadType('SomeInterface')
];
}
]);
case 'OtherObject':
return $this->OtherObjectType ?: $this->OtherObjectType = new ObjectType([
'name' => 'OtherObject',
'fields' => function() {
$this->calls[] = 'OtherObject.fields';
return [
'union' => ['type' => $this->loadType('SomeUnion')],
'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))],
];
}
]);
case 'DeeperObject':
return $this->DeeperObjectType ?: $this->DeeperObjectType = new ObjectType([
'name' => 'DeeperObject',
'fields' => function() {
return [
'scalar' => ['type' => $this->loadType('SomeScalar')],
];
}
]);
case 'SomeScalar';
return $this->SomeScalarType ?: $this->SomeScalarType = new CustomScalarType([
'name' => 'SomeScalar',
'serialize' => function($value) {return $value;},
'parseValue' => function($value) {return $value;},
'parseLiteral' => function() {}
]);
case 'SomeUnion':
return $this->SomeUnionType ?: $this->SomeUnionType = new UnionType([
'name' => 'SomeUnion',
'resolveType' => function() {
$this->calls[] = 'SomeUnion.resolveType';
return $this->loadType('DeeperObject');
},
'types' => function() {
$this->calls[] = 'SomeUnion.types';
return [ $this->loadType('DeeperObject') ];
}
]);
case 'SomeInterface':
return $this->SomeInterfaceType ?: $this->SomeInterfaceType = new InterfaceType([
'name' => 'SomeInterface',
'resolveType' => function() {
$this->calls[] = 'SomeInterface.resolveType';
return $this->loadType('SomeObject');
},
'fields' => function() {
$this->calls[] = 'SomeInterface.fields';
return [
'string' => ['type' => Type::string() ]
];
}
]);
default:
return null;
}
}
} }

View File

@ -1,17 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function sprintf;
class ExecutorSchemaTest extends TestCase class ExecutorSchemaTest extends TestCase
{ {
// Execute: Handles execution with a complex schema // Execute: Handles execution with a complex schema
/** /**
* @see it('executes using a schema') * @see it('executes using a schema')
*/ */
@ -24,7 +27,7 @@ class ExecutorSchemaTest extends TestCase
'url' => ['type' => Type::string()], 'url' => ['type' => Type::string()],
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()], 'height' => ['type' => Type::int()],
] ],
]); ]);
$BlogAuthor = new ObjectType([ $BlogAuthor = new ObjectType([
@ -38,11 +41,11 @@ class ExecutorSchemaTest extends TestCase
'type' => $BlogImage, 'type' => $BlogImage,
'resolve' => function ($obj, $args) { 'resolve' => function ($obj, $args) {
return $obj['pic']($args['width'], $args['height']); return $obj['pic']($args['width'], $args['height']);
} },
], ],
'recentArticle' => $BlogArticle 'recentArticle' => $BlogArticle,
]; ];
} },
]); ]);
$BlogArticle = new ObjectType([ $BlogArticle = new ObjectType([
@ -53,8 +56,8 @@ class ExecutorSchemaTest extends TestCase
'author' => ['type' => $BlogAuthor], 'author' => ['type' => $BlogAuthor],
'title' => ['type' => Type::string()], 'title' => ['type' => Type::string()],
'body' => ['type' => Type::string()], 'body' => ['type' => Type::string()],
'keywords' => ['type' => Type::listOf(Type::string())] 'keywords' => ['type' => Type::listOf(Type::string())],
] ],
]); ]);
$BlogQuery = new ObjectType([ $BlogQuery = new ObjectType([
@ -65,7 +68,7 @@ class ExecutorSchemaTest extends TestCase
'args' => ['id' => ['type' => Type::id()]], 'args' => ['id' => ['type' => Type::id()]],
'resolve' => function ($_, $args) { 'resolve' => function ($_, $args) {
return $this->article($args['id']); return $this->article($args['id']);
} },
], ],
'feed' => [ 'feed' => [
'type' => Type::listOf($BlogArticle), 'type' => Type::listOf($BlogArticle),
@ -80,16 +83,15 @@ class ExecutorSchemaTest extends TestCase
$this->article(7), $this->article(7),
$this->article(8), $this->article(8),
$this->article(9), $this->article(9),
$this->article(10) $this->article(10),
]; ];
} },
] ],
] ],
]); ]);
$BlogSchema = new Schema(['query' => $BlogQuery]); $BlogSchema = new Schema(['query' => $BlogQuery]);
$request = ' $request = '
{ {
feed { feed {
@ -127,26 +129,46 @@ class ExecutorSchemaTest extends TestCase
$expected = [ $expected = [
'data' => [ 'data' => [
'feed' => [ 'feed' => [
['id' => '1', [
'title' => 'My Article 1'], 'id' => '1',
['id' => '2', 'title' => 'My Article 1',
'title' => 'My Article 2'], ],
['id' => '3', [
'title' => 'My Article 3'], 'id' => '2',
['id' => '4', 'title' => 'My Article 2',
'title' => 'My Article 4'], ],
['id' => '5', [
'title' => 'My Article 5'], 'id' => '3',
['id' => '6', 'title' => 'My Article 3',
'title' => 'My Article 6'], ],
['id' => '7', [
'title' => 'My Article 7'], 'id' => '4',
['id' => '8', 'title' => 'My Article 4',
'title' => 'My Article 8'], ],
['id' => '9', [
'title' => 'My Article 9'], 'id' => '5',
['id' => '10', 'title' => 'My Article 5',
'title' => 'My Article 10'] ],
[
'id' => '6',
'title' => 'My Article 6',
],
[
'id' => '7',
'title' => 'My Article 7',
],
[
'id' => '8',
'title' => 'My Article 8',
],
[
'id' => '9',
'title' => 'My Article 9',
],
[
'id' => '10',
'title' => 'My Article 10',
],
], ],
'article' => [ 'article' => [
'id' => '1', 'id' => '1',
@ -159,18 +181,18 @@ class ExecutorSchemaTest extends TestCase
'pic' => [ 'pic' => [
'url' => 'cdn://123', 'url' => 'cdn://123',
'width' => 640, 'width' => 640,
'height' => 480 'height' => 480,
], ],
'recentArticle' => [ 'recentArticle' => [
'id' => '1', 'id' => '1',
'isPublished' => true, 'isPublished' => true,
'title' => 'My Article 1', 'title' => 'My Article 1',
'body' => 'This is a post', 'body' => 'This is a post',
'keywords' => ['foo', 'bar', '1', 'true', null] 'keywords' => ['foo', 'bar', '1', 'true', null],
] ],
] ],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray()); $this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray());
@ -187,22 +209,24 @@ class ExecutorSchemaTest extends TestCase
'title' => 'My Article ' . $id, 'title' => 'My Article ' . $id,
'body' => 'This is a post', 'body' => 'This is a post',
'hidden' => 'This data is not exposed in the schema', 'hidden' => 'This data is not exposed in the schema',
'keywords' => ['foo', 'bar', 1, true, null] 'keywords' => ['foo', 'bar', 1, true, null],
]; ];
}; };
$getPic = function ($uid, $width, $height) { $getPic = function ($uid, $width, $height) {
return [ return [
'url' => "cdn://$uid", 'url' => sprintf('cdn://%s', $uid),
'width' => $width, 'width' => $width,
'height' => $height 'height' => $height,
]; ];
}; };
$johnSmith = [ $johnSmith = [
'id' => 123, 'id' => 123,
'name' => 'John Smith', 'name' => 'John Smith',
'pic' => function($width, $height) use ($getPic) {return $getPic(123, $width, $height);}, 'pic' => function ($width, $height) use ($getPic) {
return $getPic(123, $width, $height);
},
'recentArticle' => $article(1), 'recentArticle' => $article(1),
]; ];

View File

@ -1,20 +1,25 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
require_once __DIR__ . '/TestClasses.php';
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Error\Error;
use GraphQL\Error\UserError; use GraphQL\Error\UserError;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\NotSpecial;
use GraphQL\Tests\Executor\TestClasses\Special;
use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function array_keys;
use function count;
use function json_encode;
class ExecutorTest extends TestCase class ExecutorTest extends TestCase
{ {
@ -40,11 +45,21 @@ class ExecutorTest extends TestCase
}; };
$data = [ $data = [
'a' => function () { return 'Apple';}, 'a' => function () {
'b' => function () {return 'Banana';}, return 'Apple';
'c' => function () {return 'Cookie';}, },
'd' => function () {return 'Donut';}, 'b' => function () {
'e' => function () {return 'Egg';}, return 'Banana';
},
'c' => function () {
return 'Cookie';
},
'd' => function () {
return 'Donut';
},
'e' => function () {
return 'Egg';
},
'f' => 'Fish', 'f' => 'Fish',
'pic' => function ($size = 50) { 'pic' => function ($size = 50) {
return 'Pic of size: ' . $size; return 'Pic of size: ' . $size;
@ -54,21 +69,25 @@ class ExecutorTest extends TestCase
}, },
'deep' => function () use (&$deepData) { 'deep' => function () use (&$deepData) {
return $deepData; return $deepData;
} },
]; ];
// Required for that & reference above
$deepData = [ $deepData = [
'a' => function () { return 'Already Been Done'; }, 'a' => function () {
'b' => function () { return 'Boring'; }, return 'Already Been Done';
},
'b' => function () {
return 'Boring';
},
'c' => function () { 'c' => function () {
return ['Contrived', null, 'Confusing']; return ['Contrived', null, 'Confusing'];
}, },
'deeper' => function () use (&$data) { 'deeper' => function () use (&$data) {
return [$data, null, $data]; return [$data, null, $data];
} },
]; ];
$doc = ' $doc = '
query Example($size: Int) { query Example($size: Int) {
a, a,
@ -109,9 +128,7 @@ class ExecutorTest extends TestCase
'e' => 'Egg', 'e' => 'Egg',
'f' => 'Fish', 'f' => 'Fish',
'pic' => 'Pic of size: 100', 'pic' => 'Pic of size: 100',
'promise' => [ 'promise' => ['a' => 'Apple'],
'a' => 'Apple'
],
'deep' => [ 'deep' => [
'a' => 'Already Been Done', 'a' => 'Already Been Done',
'b' => 'Boring', 'b' => 'Boring',
@ -119,10 +136,10 @@ class ExecutorTest extends TestCase
'deeper' => [ 'deeper' => [
['a' => 'Apple', 'b' => 'Banana'], ['a' => 'Apple', 'b' => 'Banana'],
null, null,
[ 'a' => 'Apple', 'b' => 'Banana' ] ['a' => 'Apple', 'b' => 'Banana'],
] ],
] ],
] ],
]; ];
$deepDataType = null; $deepDataType = null;
@ -141,26 +158,30 @@ class ExecutorTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($obj, $args) { 'resolve' => function ($obj, $args) {
return $obj['pic']($args['size']); return $obj['pic']($args['size']);
} },
], ],
'promise' => ['type' => $dataType], 'promise' => ['type' => $dataType],
'deep' => ['type' => $deepDataType], 'deep' => ['type' => $deepDataType],
]; ];
} },
]); ]);
// Required for that & reference above
$deepDataType = new ObjectType([ $deepDataType = new ObjectType([
'name' => 'DeepDataType', 'name' => 'DeepDataType',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
'b' => ['type' => Type::string()], 'b' => ['type' => Type::string()],
'c' => ['type' => Type::listOf(Type::string())], 'c' => ['type' => Type::listOf(Type::string())],
'deeper' => [ 'type' => Type::listOf($dataType) ] 'deeper' => ['type' => Type::listOf($dataType)],
] ],
]); ]);
$schema = new Schema(['query' => $dataType]); $schema = new Schema(['query' => $dataType]);
$this->assertEquals($expected, Executor::execute($schema, $ast, $data, null, ['size' => 100], 'Example')->toArray()); $this->assertEquals(
$expected,
Executor::execute($schema, $ast, $data, null, ['size' => 100], 'Example')->toArray()
);
} }
/** /**
@ -186,24 +207,34 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => function () use (&$Type) { 'fields' => function () use (&$Type) {
return [ return [
'a' => ['type' => Type::string(), 'resolve' => function () { 'a' => [
'type' => Type::string(),
'resolve' => function () {
return 'Apple'; return 'Apple';
}], },
'b' => ['type' => Type::string(), 'resolve' => function () { ],
'b' => [
'type' => Type::string(),
'resolve' => function () {
return 'Banana'; return 'Banana';
}], },
'c' => ['type' => Type::string(), 'resolve' => function () { ],
'c' => [
'type' => Type::string(),
'resolve' => function () {
return 'Cherry'; return 'Cherry';
}], },
],
'deep' => [ 'deep' => [
'type' => $Type, 'type' => $Type,
'resolve' => function () { 'resolve' => function () {
return []; return [];
} },
] ],
]; ];
} },
]); ]);
$schema = new Schema(['query' => $Type]); $schema = new Schema(['query' => $Type]);
$expected = [ $expected = [
'data' => [ 'data' => [
@ -215,10 +246,10 @@ class ExecutorTest extends TestCase
'c' => 'Cherry', 'c' => 'Cherry',
'deeper' => [ 'deeper' => [
'b' => 'Banana', 'b' => 'Banana',
'c' => 'Cherry' 'c' => 'Cherry',
] ],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($schema, $ast)->toArray()); $this->assertEquals($expected, Executor::execute($schema, $ast)->toArray());
@ -241,17 +272,18 @@ class ExecutorTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($val, $args, $ctx, $_info) use (&$info) { 'resolve' => function ($val, $args, $ctx, $_info) use (&$info) {
$info = $_info; $info = $_info;
} },
] ],
] ],
]) ]),
]); ]);
$rootValue = ['root' => 'val']; $rootValue = ['root' => 'val'];
Executor::execute($schema, $ast, $rootValue, null, ['var' => '123']); Executor::execute($schema, $ast, $rootValue, null, ['var' => '123']);
$this->assertEquals([ $this->assertEquals(
[
'fieldName', 'fieldName',
'fieldNodes', 'fieldNodes',
'returnType', 'returnType',
@ -262,7 +294,9 @@ class ExecutorTest extends TestCase
'rootValue', 'rootValue',
'operation', 'operation',
'variableValues', 'variableValues',
], array_keys((array) $info)); ],
array_keys((array) $info)
);
$this->assertEquals('test', $info->fieldName); $this->assertEquals('test', $info->fieldName);
$this->assertEquals(1, count($info->fieldNodes)); $this->assertEquals(1, count($info->fieldNodes));
@ -286,9 +320,7 @@ class ExecutorTest extends TestCase
$gotHere = false; $gotHere = false;
$data = [ $data = ['contextThing' => 'thing'];
'contextThing' => 'thing',
];
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$schema = new Schema([ $schema = new Schema([
@ -300,10 +332,10 @@ class ExecutorTest extends TestCase
'resolve' => function ($context) use ($doc, &$gotHere) { 'resolve' => function ($context) use ($doc, &$gotHere) {
$this->assertEquals('thing', $context['contextThing']); $this->assertEquals('thing', $context['contextThing']);
$gotHere = true; $gotHere = true;
} },
] ],
] ],
]) ]),
]); ]);
Executor::execute($schema, $ast, $data, null, [], 'Example'); Executor::execute($schema, $ast, $data, null, [], 'Example');
@ -331,17 +363,17 @@ class ExecutorTest extends TestCase
'b' => [ 'b' => [
'args' => [ 'args' => [
'numArg' => ['type' => Type::int()], 'numArg' => ['type' => Type::int()],
'stringArg' => ['type' => Type::string()] 'stringArg' => ['type' => Type::string()],
], ],
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($_, $args) use (&$gotHere) { 'resolve' => function ($_, $args) use (&$gotHere) {
$this->assertEquals(123, $args['numArg']); $this->assertEquals(123, $args['numArg']);
$this->assertEquals('foo', $args['stringArg']); $this->assertEquals('foo', $args['stringArg']);
$gotHere = true; $gotHere = true;
} },
] ],
] ],
]) ]),
]); ]);
Executor::execute($schema, $docAst, null, null, [], 'Example'); Executor::execute($schema, $docAst, null, null, [], 'Example');
$this->assertSame($gotHere, true); $this->assertSame($gotHere, true);
@ -387,14 +419,18 @@ class ExecutorTest extends TestCase
'sync0', 'sync0',
new UserError('Error getting syncReturnErrorList1'), new UserError('Error getting syncReturnErrorList1'),
'sync2', 'sync2',
new UserError('Error getting syncReturnErrorList3') new UserError('Error getting syncReturnErrorList3'),
]; ];
}, },
'async' => function () { 'async' => function () {
return new Deferred(function() { return 'async'; }); return new Deferred(function () {
return 'async';
});
}, },
'asyncReject' => function () { 'asyncReject' => function () {
return new Deferred(function() { throw new UserError('Error getting asyncReject'); }); return new Deferred(function () {
throw new UserError('Error getting asyncReject');
});
}, },
'asyncRawReject' => function () { 'asyncRawReject' => function () {
return new Deferred(function () { return new Deferred(function () {
@ -442,8 +478,8 @@ class ExecutorTest extends TestCase
'asyncError' => ['type' => Type::string()], 'asyncError' => ['type' => Type::string()],
'asyncRawError' => ['type' => Type::string()], 'asyncRawError' => ['type' => Type::string()],
'asyncReturnError' => ['type' => Type::string()], 'asyncReturnError' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$expected = [ $expected = [
@ -465,59 +501,59 @@ class ExecutorTest extends TestCase
[ [
'message' => 'Error getting syncError', 'message' => 'Error getting syncError',
'locations' => [['line' => 3, 'column' => 7]], 'locations' => [['line' => 3, 'column' => 7]],
'path' => ['syncError'] 'path' => ['syncError'],
], ],
[ [
'message' => 'Error getting syncRawError', 'message' => 'Error getting syncRawError',
'locations' => [['line' => 4, 'column' => 7]], 'locations' => [['line' => 4, 'column' => 7]],
'path'=> [ 'syncRawError' ] 'path' => ['syncRawError'],
], ],
[ [
'message' => 'Error getting syncReturnError', 'message' => 'Error getting syncReturnError',
'locations' => [['line' => 5, 'column' => 7]], 'locations' => [['line' => 5, 'column' => 7]],
'path' => ['syncReturnError'] 'path' => ['syncReturnError'],
], ],
[ [
'message' => 'Error getting syncReturnErrorList1', 'message' => 'Error getting syncReturnErrorList1',
'locations' => [['line' => 6, 'column' => 7]], 'locations' => [['line' => 6, 'column' => 7]],
'path' => ['syncReturnErrorList', 1] 'path' => ['syncReturnErrorList', 1],
], ],
[ [
'message' => 'Error getting syncReturnErrorList3', 'message' => 'Error getting syncReturnErrorList3',
'locations' => [['line' => 6, 'column' => 7]], 'locations' => [['line' => 6, 'column' => 7]],
'path' => ['syncReturnErrorList', 3] 'path' => ['syncReturnErrorList', 3],
], ],
[ [
'message' => 'Error getting asyncReject', 'message' => 'Error getting asyncReject',
'locations' => [['line' => 8, 'column' => 7]], 'locations' => [['line' => 8, 'column' => 7]],
'path' => ['asyncReject'] 'path' => ['asyncReject'],
], ],
[ [
'message' => 'Error getting asyncRawReject', 'message' => 'Error getting asyncRawReject',
'locations' => [['line' => 9, 'column' => 7]], 'locations' => [['line' => 9, 'column' => 7]],
'path' => ['asyncRawReject'] 'path' => ['asyncRawReject'],
], ],
[ [
'message' => 'An unknown error occurred.', 'message' => 'An unknown error occurred.',
'locations' => [['line' => 10, 'column' => 7]], 'locations' => [['line' => 10, 'column' => 7]],
'path' => ['asyncEmptyReject'] 'path' => ['asyncEmptyReject'],
], ],
[ [
'message' => 'Error getting asyncError', 'message' => 'Error getting asyncError',
'locations' => [['line' => 11, 'column' => 7]], 'locations' => [['line' => 11, 'column' => 7]],
'path' => ['asyncError'] 'path' => ['asyncError'],
], ],
[ [
'message' => 'Error getting asyncRawError', 'message' => 'Error getting asyncRawError',
'locations' => [['line' => 12, 'column' => 7]], 'locations' => [['line' => 12, 'column' => 7]],
'path' => [ 'asyncRawError' ] 'path' => ['asyncRawError'],
], ],
[ [
'message' => 'Error getting asyncReturnError', 'message' => 'Error getting asyncReturnError',
'locations' => [['line' => 13, 'column' => 7]], 'locations' => [['line' => 13, 'column' => 7]],
'path' => ['asyncReturnError'] 'path' => ['asyncReturnError'],
],
], ],
]
]; ];
$result = Executor::execute($schema, $docAst, $data)->toArray(); $result = Executor::execute($schema, $docAst, $data)->toArray();
@ -538,8 +574,8 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$ex = Executor::execute($schema, $ast, $data); $ex = Executor::execute($schema, $ast, $data);
@ -560,8 +596,8 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$ex = Executor::execute($schema, $ast, $data); $ex = Executor::execute($schema, $ast, $data);
@ -581,8 +617,8 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$result = Executor::execute($schema, $ast, $data, null, null, 'OtherExample'); $result = Executor::execute($schema, $ast, $data, null, null, 'OtherExample');
@ -602,17 +638,15 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$result = Executor::execute($schema, $ast, $data); $result = Executor::execute($schema, $ast, $data);
$expected = [ $expected = [
'errors' => [ 'errors' => [
[ ['message' => 'Must provide an operation.'],
'message' => 'Must provide an operation.', ],
]
]
]; ];
$this->assertArraySubset($expected, $result->toArray()); $this->assertArraySubset($expected, $result->toArray());
@ -631,18 +665,16 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$result = Executor::execute($schema, $ast, $data); $result = Executor::execute($schema, $ast, $data);
$expected = [ $expected = [
'errors' => [ 'errors' => [
[ ['message' => 'Must provide operation name if query contains multiple operations.'],
'message' => 'Must provide operation name if query contains multiple operations.', ],
]
]
]; ];
$this->assertArraySubset($expected, $result->toArray()); $this->assertArraySubset($expected, $result->toArray());
@ -660,11 +692,10 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$result = Executor::execute( $result = Executor::execute(
$schema, $schema,
$ast, $ast,
@ -676,10 +707,8 @@ class ExecutorTest extends TestCase
$expected = [ $expected = [
'errors' => [ 'errors' => [
[ ['message' => 'Unknown operation named "UnknownExample".'],
'message' => 'Unknown operation named "UnknownExample".', ],
]
]
]; ];
@ -699,14 +728,14 @@ class ExecutorTest extends TestCase
'name' => 'Q', 'name' => 'Q',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]), ]),
'mutation' => new ObjectType([ 'mutation' => new ObjectType([
'name' => 'M', 'name' => 'M',
'fields' => [ 'fields' => [
'c' => ['type' => Type::string()], 'c' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q'); $queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q');
@ -726,14 +755,14 @@ class ExecutorTest extends TestCase
'name' => 'Q', 'name' => 'Q',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]), ]),
'mutation' => new ObjectType([ 'mutation' => new ObjectType([
'name' => 'M', 'name' => 'M',
'fields' => [ 'fields' => [
'c' => ['type' => Type::string()], 'c' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$mutationResult = Executor::execute($schema, $ast, $data, null, [], 'M'); $mutationResult = Executor::execute($schema, $ast, $data, null, [], 'M');
$this->assertEquals(['data' => ['c' => 'd']], $mutationResult->toArray()); $this->assertEquals(['data' => ['c' => 'd']], $mutationResult->toArray());
@ -752,14 +781,14 @@ class ExecutorTest extends TestCase
'name' => 'Q', 'name' => 'Q',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]), ]),
'subscription' => new ObjectType([ 'subscription' => new ObjectType([
'name' => 'S', 'name' => 'S',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$subscriptionResult = Executor::execute($schema, $ast, $data, null, [], 'S'); $subscriptionResult = Executor::execute($schema, $ast, $data, null, [], 'S');
@ -780,13 +809,17 @@ class ExecutorTest extends TestCase
return 'a'; return 'a';
}, },
'b' => function () { 'b' => function () {
return new Deferred(function () { return 'b'; }); return new Deferred(function () {
return 'b';
});
}, },
'c' => function () { 'c' => function () {
return 'c'; return 'c';
}, },
'd' => function () { 'd' => function () {
return new Deferred(function () { return 'd'; }); return new Deferred(function () {
return 'd';
});
}, },
'e' => function () { 'e' => function () {
return 'e'; return 'e';
@ -803,7 +836,7 @@ class ExecutorTest extends TestCase
'c' => ['type' => Type::string()], 'c' => ['type' => Type::string()],
'd' => ['type' => Type::string()], 'd' => ['type' => Type::string()],
'e' => ['type' => Type::string()], 'e' => ['type' => Type::string()],
] ],
]); ]);
$schema = new Schema(['query' => $queryType]); $schema = new Schema(['query' => $queryType]);
@ -814,7 +847,7 @@ class ExecutorTest extends TestCase
'c' => 'c', 'c' => 'c',
'd' => 'd', 'd' => 'd',
'e' => 'e', 'e' => 'e',
] ],
]; ];
$this->assertEquals($expected, Executor::execute($schema, $ast, $data)->toArray()); $this->assertEquals($expected, Executor::execute($schema, $ast, $data)->toArray());
@ -844,8 +877,8 @@ class ExecutorTest extends TestCase
'name' => 'Type', 'name' => 'Type',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q'); $queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q');
@ -866,14 +899,14 @@ class ExecutorTest extends TestCase
'name' => 'Q', 'name' => 'Q',
'fields' => [ 'fields' => [
'a' => ['type' => Type::string()], 'a' => ['type' => Type::string()],
] ],
]), ]),
'mutation' => new ObjectType([ 'mutation' => new ObjectType([
'name' => 'M', 'name' => 'M',
'fields' => [ 'fields' => [
'c' => ['type' => Type::string()], 'c' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$mutationResult = Executor::execute($schema, $ast); $mutationResult = Executor::execute($schema, $ast);
$this->assertEquals(['data' => []], $mutationResult->toArray()); $this->assertEquals(['data' => []], $mutationResult->toArray());
@ -890,25 +923,25 @@ class ExecutorTest extends TestCase
'fields' => [ 'fields' => [
'field' => [ 'field' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function($data, $args) {return $args ? json_encode($args) : '';}, 'resolve' => function ($data, $args) {
return $args ? json_encode($args) : '';
},
'args' => [ 'args' => [
'a' => ['type' => Type::boolean()], 'a' => ['type' => Type::boolean()],
'b' => ['type' => Type::boolean()], 'b' => ['type' => Type::boolean()],
'c' => ['type' => Type::boolean()], 'c' => ['type' => Type::boolean()],
'd' => ['type' => Type::int()], 'd' => ['type' => Type::int()],
'e' => ['type' => Type::int()] 'e' => ['type' => Type::int()],
] ],
] ],
] ],
]) ]),
]); ]);
$query = Parser::parse('{ field(a: true, c: false, e: 0) }'); $query = Parser::parse('{ field(a: true, c: false, e: 0) }');
$result = Executor::execute($schema, $query); $result = Executor::execute($schema, $query);
$expected = [ $expected = [
'data' => [ 'data' => ['field' => '{"a":true,"c":false,"e":0}'],
'field' => '{"a":true,"c":false,"e":0}'
]
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -925,8 +958,8 @@ class ExecutorTest extends TestCase
return $obj instanceof Special; return $obj instanceof Special;
}, },
'fields' => [ 'fields' => [
'value' => ['type' => Type::string()] 'value' => ['type' => Type::string()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
@ -937,31 +970,37 @@ class ExecutorTest extends TestCase
'type' => Type::listOf($SpecialType), 'type' => Type::listOf($SpecialType),
'resolve' => function ($rootValue) { 'resolve' => function ($rootValue) {
return $rootValue['specials']; return $rootValue['specials'];
} },
] ],
] ],
]) ]),
]); ]);
$query = Parser::parse('{ specials { value } }'); $query = Parser::parse('{ specials { value } }');
$value = [ $value = [
'specials' => [ new Special('foo'), new NotSpecial('bar') ] 'specials' => [new Special('foo'), new NotSpecial('bar')],
]; ];
$result = Executor::execute($schema, $query, $value); $result = Executor::execute($schema, $query, $value);
$this->assertEquals([ $this->assertEquals(
[
'specials' => [ 'specials' => [
['value' => 'foo'], ['value' => 'foo'],
null null,
] ],
], $result->data); ],
$result->data
);
$this->assertEquals(1, count($result->errors)); $this->assertEquals(1, count($result->errors));
$this->assertEquals([ $this->assertEquals(
'message' => 'Expected value of type "SpecialType" but got: instance of GraphQL\Tests\Executor\NotSpecial.', [
'message' => 'Expected value of type "SpecialType" but got: instance of GraphQL\Tests\Executor\TestClasses\NotSpecial.',
'locations' => [['line' => 1, 'column' => 3]], 'locations' => [['line' => 1, 'column' => 3]],
'path' => ['specials', 1] 'path' => ['specials', 1],
], $result->errors[0]->toSerializableArray()); ],
$result->errors[0]->toSerializableArray()
);
} }
/** /**
@ -979,18 +1018,15 @@ class ExecutorTest extends TestCase
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'foo' => ['type' => Type::string()] 'foo' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
$result = Executor::execute($schema, $query); $result = Executor::execute($schema, $query);
$expected = [ $expected = [
'data' => [ 'data' => ['foo' => null],
'foo' => null,
],
]; ];
$this->assertArraySubset($expected, $result->toArray()); $this->assertArraySubset($expected, $result->toArray());
@ -1007,9 +1043,9 @@ class ExecutorTest extends TestCase
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'foo' => ['type' => Type::string()] 'foo' => ['type' => Type::string()],
] ],
]) ]),
]); ]);
// For the purposes of test, just return the name of the field! // For the purposes of test, just return the name of the field!
@ -1028,7 +1064,7 @@ class ExecutorTest extends TestCase
); );
$expected = [ $expected = [
'data' => ['foo' => 'foo'] 'data' => ['foo' => 'foo'],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -1042,7 +1078,9 @@ class ExecutorTest extends TestCase
'fields' => [ 'fields' => [
'field' => [ 'field' => [
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function($data, $args) {return $args ? json_encode($args) : '';}, 'resolve' => function ($data, $args) {
return $args ? json_encode($args) : '';
},
'args' => [ 'args' => [
'a' => ['type' => Type::boolean(), 'defaultValue' => 1], 'a' => ['type' => Type::boolean(), 'defaultValue' => 1],
'b' => ['type' => Type::boolean(), 'defaultValue' => null], 'b' => ['type' => Type::boolean(), 'defaultValue' => null],
@ -1051,25 +1089,25 @@ class ExecutorTest extends TestCase
'e' => ['type' => Type::int(), 'defaultValue' => '0'], 'e' => ['type' => Type::int(), 'defaultValue' => '0'],
'f' => ['type' => Type::int(), 'defaultValue' => 'some-string'], 'f' => ['type' => Type::int(), 'defaultValue' => 'some-string'],
'g' => ['type' => Type::boolean()], 'g' => ['type' => Type::boolean()],
'h' => ['type' => new InputObjectType([ 'h' => [
'type' => new InputObjectType([
'name' => 'ComplexType', 'name' => 'ComplexType',
'fields' => [ 'fields' => [
'a' => ['type' => Type::int()], 'a' => ['type' => Type::int()],
'b' => ['type' => Type::string()] 'b' => ['type' => Type::string()],
] ],
]), 'defaultValue' => ['a' => 1, 'b' => 'test']] ]), 'defaultValue' => ['a' => 1, 'b' => 'test'],
] ],
] ],
] ],
]) ],
]),
]); ]);
$query = Parser::parse('{ field }'); $query = Parser::parse('{ field }');
$result = Executor::execute($schema, $query); $result = Executor::execute($schema, $query);
$expected = [ $expected = [
'data' => [ 'data' => ['field' => '{"a":1,"b":null,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}'],
'field' => '{"a":1,"b":null,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}'
]
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -1085,41 +1123,41 @@ class ExecutorTest extends TestCase
$a = new ObjectType([ $a = new ObjectType([
'name' => 'A', 'name' => 'A',
'fields' => [ 'fields' => [
'id' => Type::id() 'id' => Type::id(),
], ],
'interfaces' => function () use (&$iface) { 'interfaces' => function () use (&$iface) {
return [$iface]; return [$iface];
} },
]); ]);
$b = new ObjectType([ $b = new ObjectType([
'name' => 'B', 'name' => 'B',
'fields' => [ 'fields' => [
'id' => Type::id() 'id' => Type::id(),
], ],
'interfaces' => function () use (&$iface) { 'interfaces' => function () use (&$iface) {
return [$iface]; return [$iface];
} },
]); ]);
$iface = new InterfaceType([ $iface = new InterfaceType([
'name' => 'Iface', 'name' => 'Iface',
'fields' => [ 'fields' => [
'id' => Type::id() 'id' => Type::id(),
], ],
'resolveType' => function ($v) use ($a, $b) { 'resolveType' => function ($v) use ($a, $b) {
return $v['type'] === 'A' ? $a : $b; return $v['type'] === 'A' ? $a : $b;
} },
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'ab' => Type::listOf($iface) 'ab' => Type::listOf($iface),
] ],
]), ]),
'types' => [$a, $b] 'types' => [$a, $b],
]); ]);
$data = [ $data = [
@ -1127,8 +1165,8 @@ class ExecutorTest extends TestCase
['id' => 1, 'type' => 'A'], ['id' => 1, 'type' => 'A'],
['id' => 2, 'type' => 'A'], ['id' => 2, 'type' => 'A'],
['id' => 3, 'type' => 'B'], ['id' => 3, 'type' => 'B'],
['id' => 4, 'type' => 'B'] ['id' => 4, 'type' => 'B'],
] ],
]; ];
$query = Parser::parse(' $query = Parser::parse('
@ -1143,15 +1181,18 @@ class ExecutorTest extends TestCase
$result = Executor::execute($schema, $query, $data, null); $result = Executor::execute($schema, $query, $data, null);
$this->assertEquals([ $this->assertEquals(
[
'data' => [ 'data' => [
'ab' => [ 'ab' => [
['id' => '1'], ['id' => '1'],
['id' => '2'], ['id' => '2'],
new \stdClass(), new \stdClass(),
new \stdClass() new \stdClass(),
] ],
] ],
], $result->toArray()); ],
$result->toArray()
);
} }
} }

View File

@ -1,36 +1,58 @@
<?php <?php
declare(strict_types=1);
/** /**
* @author: Ivo Meißner * @author: Ivo Meißner
* Date: 03.05.16 * Date: 03.05.16
* Time: 13:14 * Time: 13:14
*/ */
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class LazyInterfaceTest extends TestCase class LazyInterfaceTest extends TestCase
{ {
/** /** @var Schema */
* @var Schema
*/
protected $schema; protected $schema;
/** /** @var InterfaceType */
* @var InterfaceType
*/
protected $lazyInterface; protected $lazyInterface;
/** /** @var ObjectType */
* @var ObjectType
*/
protected $testObject; protected $testObject;
/**
* Handles execution of a lazily created interface
*/
public function testReturnsFragmentsWithLazyCreatedInterface() : void
{
$request = '
{
lazyInterface {
... on TestObject {
name
}
}
}
';
$expected = [
'data' => [
'lazyInterface' => ['name' => 'testname'],
],
];
$this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray());
}
/** /**
* Setup schema * Setup schema
*/ */
@ -44,10 +66,10 @@ class LazyInterfaceTest extends TestCase
'type' => $this->getLazyInterfaceType(), 'type' => $this->getLazyInterfaceType(),
'resolve' => function () { 'resolve' => function () {
return []; return [];
} },
] ],
]; ];
} },
]); ]);
$this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]); $this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]);
@ -64,7 +86,7 @@ class LazyInterfaceTest extends TestCase
$this->lazyInterface = new InterfaceType([ $this->lazyInterface = new InterfaceType([
'name' => 'LazyInterface', 'name' => 'LazyInterface',
'fields' => [ 'fields' => [
'a' => Type::string() 'a' => Type::string(),
], ],
'resolveType' => function () { 'resolveType' => function () {
return $this->getTestObjectType(); return $this->getTestObjectType();
@ -89,39 +111,13 @@ class LazyInterfaceTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function () { 'resolve' => function () {
return 'testname'; return 'testname';
} },
]
], ],
'interfaces' => [$this->getLazyInterfaceType()] ],
'interfaces' => [$this->getLazyInterfaceType()],
]); ]);
} }
return $this->testObject; return $this->testObject;
} }
/**
* Handles execution of a lazily created interface
*/
public function testReturnsFragmentsWithLazyCreatedInterface() : void
{
$request = '
{
lazyInterface {
... on TestObject {
name
}
}
}
';
$expected = [
'data' => [
'lazyInterface' => [
'name' => 'testname'
]
]
];
$this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray());
}
} }

View File

@ -1,22 +1,21 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Error\UserError; use GraphQL\Error\UserError;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class ListsTest extends TestCase class ListsTest extends TestCase
{ {
// Describe: Execute: Handles list nullability // Describe: Execute: Handles list nullability
/** /**
* [T] * [T]
*/ */
@ -41,6 +40,40 @@ class ListsTest extends TestCase
); );
} }
private function checkHandlesNullableLists($testData, $expected)
{
$testType = Type::listOf(Type::int());
$this->check($testType, $testData, $expected);
}
private function check($testType, $testData, $expected, $debug = false)
{
$data = ['test' => $testData];
$dataType = null;
$dataType = new ObjectType([
'name' => 'DataType',
'fields' => function () use (&$testType, &$dataType, $data) {
return [
'test' => ['type' => $testType],
'nest' => [
'type' => $dataType,
'resolve' => function () use ($data) {
return $data;
},
],
];
},
]);
$schema = new Schema(['query' => $dataType]);
$ast = Parser::parse('{ nest { test } }');
$result = Executor::execute($schema, $ast, $data);
$this->assertArraySubset($expected, $result->toArray($debug));
}
/** /**
* [T] * [T]
*/ */
@ -83,9 +116,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test'] 'path' => ['nest', 'test'],
] ],
] ],
] ]
); );
} }
@ -103,16 +136,23 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
})], }),
],
['data' => ['nest' => ['test' => [1, 2]]]] ['data' => ['nest' => ['test' => [1, 2]]]]
); );
// Contains null // Contains null
$this->checkHandlesNullableLists( $this->checkHandlesNullableLists(
[ [
new Deferred(function() {return 1;}), new Deferred(function () {
new Deferred(function() {return null;}), return 1;
new Deferred(function() {return 2;}) }),
new Deferred(function () {
return null;
}),
new Deferred(function () {
return 2;
}),
], ],
['data' => ['nest' => ['test' => [1, null, 2]]]] ['data' => ['nest' => ['test' => [1, null, 2]]]]
); );
@ -137,7 +177,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
]; ];
}, },
[ [
@ -146,9 +186,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test', 1] 'path' => ['nest', 'test', 1],
] ],
] ],
] ]
); );
} }
@ -178,14 +218,20 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [['line' => 1, 'column' => 10]] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
} }
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
{
$testType = Type::nonNull(Type::listOf(Type::int()));
$this->check($testType, $testData, $expected, $debug);
}
/** /**
* [T]! * [T]!
*/ */
@ -215,9 +261,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [['line' => 1, 'column' => 10]] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -235,9 +281,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test'] 'path' => ['nest', 'test'],
] ],
] ],
] ]
); );
} }
@ -255,7 +301,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
], ],
['data' => ['nest' => ['test' => [1, 2]]]] ['data' => ['nest' => ['test' => [1, 2]]]]
); );
@ -271,7 +317,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
], ],
['data' => ['nest' => ['test' => [1, null, 2]]]] ['data' => ['nest' => ['test' => [1, null, 2]]]]
); );
@ -288,7 +334,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
]; ];
}, },
[ [
@ -297,9 +343,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test', 1] 'path' => ['nest', 'test', 1],
] ],
] ],
] ]
); );
} }
@ -323,9 +369,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [ ['line' => 1, 'column' => 10] ] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -337,6 +383,12 @@ class ListsTest extends TestCase
); );
} }
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
{
$testType = Type::listOf(Type::nonNull(Type::int()));
$this->check($testType, $testData, $expected, $debug);
}
/** /**
* [T!] * [T!]
*/ */
@ -360,9 +412,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [['line' => 1, 'column' => 10]] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -388,9 +440,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test'] 'path' => ['nest', 'test'],
] ],
] ],
] ]
); );
} }
@ -408,7 +460,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
], ],
['data' => ['nest' => ['test' => [1, 2]]]] ['data' => ['nest' => ['test' => [1, 2]]]]
); );
@ -416,9 +468,15 @@ class ListsTest extends TestCase
// Contains null // Contains null
$this->checkHandlesListOfNonNulls( $this->checkHandlesListOfNonNulls(
[ [
new Deferred(function() {return 1;}), new Deferred(function () {
new Deferred(function() {return null;}), return 1;
new Deferred(function() {return 2;}) }),
new Deferred(function () {
return null;
}),
new Deferred(function () {
return 2;
}),
], ],
['data' => ['nest' => ['test' => null]]] ['data' => ['nest' => ['test' => null]]]
); );
@ -435,7 +493,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
]; ];
}, },
[ [
@ -444,9 +502,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test', 1] 'path' => ['nest', 'test', 1],
] ],
] ],
] ]
); );
} }
@ -462,7 +520,6 @@ class ListsTest extends TestCase
['data' => ['nest' => ['test' => [1, 2]]]] ['data' => ['nest' => ['test' => [1, 2]]]]
); );
// Contains null // Contains null
$this->checkHandlesNonNullListOfNonNulls( $this->checkHandlesNonNullListOfNonNulls(
[1, null, 2], [1, null, 2],
@ -471,9 +528,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [['line' => 1, 'column' => 10 ]] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -486,14 +543,20 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [ ['line' => 1, 'column' => 10] ] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
} }
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
{
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
$this->check($testType, $testData, $expected, $debug);
}
/** /**
* [T!]! * [T!]!
*/ */
@ -517,9 +580,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [ ['line' => 1, 'column' => 10] ] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -534,9 +597,9 @@ class ListsTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [ ['line' => 1, 'column' => 10] ] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -554,9 +617,9 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test'] 'path' => ['nest', 'test'],
] ],
] ],
] ]
); );
} }
@ -574,7 +637,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
], ],
['data' => ['nest' => ['test' => [1, 2]]]] ['data' => ['nest' => ['test' => [1, 2]]]]
@ -591,16 +654,16 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
], ],
[ [
'data' => ['nest' => null], 'data' => ['nest' => null],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
'locations' => [['line' => 1, 'column' => 10]] 'locations' => [['line' => 1, 'column' => 10]],
] ],
] ],
], ],
true true
); );
@ -617,7 +680,7 @@ class ListsTest extends TestCase
}), }),
new Deferred(function () { new Deferred(function () {
return 2; return 2;
}) }),
]; ];
}, },
[ [
@ -626,66 +689,10 @@ class ListsTest extends TestCase
[ [
'message' => 'bad', 'message' => 'bad',
'locations' => [['line' => 1, 'column' => 10]], 'locations' => [['line' => 1, 'column' => 10]],
'path' => ['nest', 'test'] 'path' => ['nest', 'test'],
] ],
] ],
] ]
); );
} }
private function checkHandlesNullableLists($testData, $expected)
{
$testType = Type::listOf(Type::int());
$this->check($testType, $testData, $expected);
}
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
{
$testType = Type::nonNull(Type::listOf(Type::int()));
$this->check($testType, $testData, $expected, $debug);
}
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
{
$testType = Type::listOf(Type::nonNull(Type::int()));
$this->check($testType, $testData, $expected, $debug);
}
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
{
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
$this->check($testType, $testData, $expected, $debug);
}
private function check($testType, $testData, $expected, $debug = false)
{
$data = ['test' => $testData];
$dataType = null;
$dataType = new ObjectType([
'name' => 'DataType',
'fields' => function () use (&$testType, &$dataType, $data) {
return [
'test' => [
'type' => $testType
],
'nest' => [
'type' => $dataType,
'resolve' => function () use ($data) {
return $data;
}
]
];
}
]);
$schema = new Schema([
'query' => $dataType
]);
$ast = Parser::parse('{ nest { test } }');
$result = Executor::execute($schema, $ast, $data);
$this->assertArraySubset($expected, $result->toArray($debug));
}
} }

View File

@ -1,21 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Deferred;
use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation; use GraphQL\Tests\Executor\TestClasses\Root;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class MutationsTest extends TestCase class MutationsTest extends TestCase
{ {
// Execute: Handles mutation execution ordering // Execute: Handles mutation execution ordering
/** /**
* @see it('evaluates mutations serially') * @see it('evaluates mutations serially')
*/ */
@ -42,26 +41,69 @@ class MutationsTest extends TestCase
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6)); $mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
$expected = [ $expected = [
'data' => [ 'data' => [
'first' => [ 'first' => ['theNumber' => 1],
'theNumber' => 1 'second' => ['theNumber' => 2],
'third' => ['theNumber' => 3],
'fourth' => ['theNumber' => 4],
'fifth' => ['theNumber' => 5],
], ],
'second' => [
'theNumber' => 2
],
'third' => [
'theNumber' => 3
],
'fourth' => [
'theNumber' => 4
],
'fifth' => [
'theNumber' => 5
]
]
]; ];
$this->assertEquals($expected, $mutationResult->toArray()); $this->assertEquals($expected, $mutationResult->toArray());
} }
private function schema() : Schema
{
$numberHolderType = new ObjectType([
'fields' => [
'theNumber' => ['type' => Type::int()],
],
'name' => 'NumberHolder',
]);
$schema = new Schema([
'query' => new ObjectType([
'fields' => [
'numberHolder' => ['type' => $numberHolderType],
],
'name' => 'Query',
]),
'mutation' => new ObjectType([
'fields' => [
'immediatelyChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => function (Root $obj, $args) {
return $obj->immediatelyChangeTheNumber($args['newNumber']);
},
],
'promiseToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => function (Root $obj, $args) {
return $obj->promiseToChangeTheNumber($args['newNumber']);
},
],
'failToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => function (Root $obj, $args) {
$obj->failToChangeTheNumber();
},
],
'promiseAndFailToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => function (Root $obj, $args) {
return $obj->promiseAndFailToChangeTheNumber();
},
],
],
'name' => 'Mutation',
]),
]);
return $schema;
}
/** /**
* @see it('evaluates mutations correctly in the presense of a failed mutation') * @see it('evaluates mutations correctly in the presense of a failed mutation')
*/ */
@ -91,143 +133,24 @@ class MutationsTest extends TestCase
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6)); $mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
$expected = [ $expected = [
'data' => [ 'data' => [
'first' => [ 'first' => ['theNumber' => 1],
'theNumber' => 1 'second' => ['theNumber' => 2],
],
'second' => [
'theNumber' => 2
],
'third' => null, 'third' => null,
'fourth' => [ 'fourth' => ['theNumber' => 4],
'theNumber' => 4 'fifth' => ['theNumber' => 5],
],
'fifth' => [
'theNumber' => 5
],
'sixth' => null, 'sixth' => null,
], ],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot change the number', 'debugMessage' => 'Cannot change the number',
'locations' => [['line' => 8, 'column' => 7]] 'locations' => [['line' => 8, 'column' => 7]],
], ],
[ [
'debugMessage' => 'Cannot change the number', 'debugMessage' => 'Cannot change the number',
'locations' => [['line' => 17, 'column' => 7]] 'locations' => [['line' => 17, 'column' => 7]],
] ],
] ],
]; ];
$this->assertArraySubset($expected, $mutationResult->toArray(true)); $this->assertArraySubset($expected, $mutationResult->toArray(true));
} }
private function schema()
{
$numberHolderType = new ObjectType([
'fields' => [
'theNumber' => ['type' => Type::int()],
],
'name' => 'NumberHolder',
]);
$schema = new Schema([
'query' => new ObjectType([
'fields' => [
'numberHolder' => ['type' => $numberHolderType],
],
'name' => 'Query',
]),
'mutation' => new ObjectType([
'fields' => [
'immediatelyChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => (function (Root $obj, $args) {
return $obj->immediatelyChangeTheNumber($args['newNumber']);
})
],
'promiseToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => (function (Root $obj, $args) {
return $obj->promiseToChangeTheNumber($args['newNumber']);
})
],
'failToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => (function (Root $obj, $args) {
return $obj->failToChangeTheNumber($args['newNumber']);
})
],
'promiseAndFailToChangeTheNumber' => [
'type' => $numberHolderType,
'args' => ['newNumber' => ['type' => Type::int()]],
'resolve' => (function (Root $obj, $args) {
return $obj->promiseAndFailToChangeTheNumber($args['newNumber']);
})
]
],
'name' => 'Mutation',
])
]);
return $schema;
}
}
class NumberHolder
{
public $theNumber;
public function __construct($originalNumber)
{
$this->theNumber = $originalNumber;
}
}
class Root {
public $numberHolder;
public function __construct($originalNumber)
{
$this->numberHolder = new NumberHolder($originalNumber);
}
/**
* @param $newNumber
* @return NumberHolder
*/
public function immediatelyChangeTheNumber($newNumber)
{
$this->numberHolder->theNumber = $newNumber;
return $this->numberHolder;
}
/**
* @param $newNumber
*
* @return Deferred
*/
public function promiseToChangeTheNumber($newNumber)
{
return new Deferred(function () use ($newNumber) {
return $this->immediatelyChangeTheNumber($newNumber);
});
}
/**
* @throws \Exception
*/
public function failToChangeTheNumber()
{
throw new \Exception('Cannot change the number');
}
/**
* @return Deferred
*/
public function promiseAndFailToChangeTheNumber()
{
return new Deferred(function () {
throw new \Exception("Cannot change the number");
});
}
} }

View File

@ -1,16 +1,18 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Error\FormattedError;
use GraphQL\Error\UserError; use GraphQL\Error\UserError;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation; use GraphQL\Language\SourceLocation;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class NonNullTest extends TestCase class NonNullTest extends TestCase
@ -27,8 +29,13 @@ class NonNullTest extends TestCase
/** @var \Exception */ /** @var \Exception */
public $promiseNonNullError; public $promiseNonNullError;
/** @var callable[] */
public $throwingData; public $throwingData;
/** @var callable[] */
public $nullingData; public $nullingData;
/** @var Schema */
public $schema; public $schema;
public function setUp() public function setUp()
@ -121,7 +128,7 @@ class NonNullTest extends TestCase
'promiseNest' => $dataType, 'promiseNest' => $dataType,
'promiseNonNullNest' => Type::nonNull($dataType), 'promiseNonNullNest' => Type::nonNull($dataType),
]; ];
} },
]); ]);
$this->schema = new Schema(['query' => $dataType]); $this->schema = new Schema(['query' => $dataType]);
@ -143,17 +150,18 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['sync' => null],
'sync' => null,
],
'errors' => [ 'errors' => [
FormattedError::create( FormattedError::create(
$this->syncError->getMessage(), $this->syncError->getMessage(),
[new SourceLocation(3, 9)] [new SourceLocation(3, 9)]
) ),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsANullableFieldThatThrowsInAPromise() : void public function testNullsANullableFieldThatThrowsInAPromise() : void
@ -167,18 +175,19 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promise' => null],
'promise' => null,
],
'errors' => [ 'errors' => [
FormattedError::create( FormattedError::create(
$this->promiseError->getMessage(), $this->promiseError->getMessage(),
[new SourceLocation(3, 9)] [new SourceLocation(3, 9)]
) ),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() : void public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() : void
@ -195,14 +204,15 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['syncNest' => null],
'syncNest' => null
],
'errors' => [ 'errors' => [
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]) FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() : void public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() : void
@ -218,15 +228,16 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['syncNest' => null],
'syncNest' => null
],
'errors' => [ 'errors' => [
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]) FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() : void public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() : void
@ -242,15 +253,16 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promiseNest' => null],
'promiseNest' => null
],
'errors' => [ 'errors' => [
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]) FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() : void public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() : void
@ -266,15 +278,16 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promiseNest' => null],
'promiseNest' => null
],
'errors' => [ 'errors' => [
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]) FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
/** /**
@ -353,10 +366,13 @@ class NonNullTest extends TestCase
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]), FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]),
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]), FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]),
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]), FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() : void public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() : void
@ -424,10 +440,13 @@ class NonNullTest extends TestCase
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(19, 19)]), FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(19, 19)]),
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(30, 19)]), FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(30, 19)]),
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(41, 19)]), FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(41, 19)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsANullableFieldThatSynchronouslyReturnsNull() : void public function testNullsANullableFieldThatSynchronouslyReturnsNull() : void
@ -441,11 +460,12 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['sync' => null],
'sync' => null,
]
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); $this->assertEquals(
$expected,
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()
);
} }
public function testNullsANullableFieldThatReturnsNullInAPromise() : void public function testNullsANullableFieldThatReturnsNullInAPromise() : void
@ -459,12 +479,13 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promise' => null],
'promise' => null,
]
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()
);
} }
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() : void public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() : void
@ -480,17 +501,18 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['syncNest' => null],
'syncNest' => null
],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
'locations' => [['line' => 4, 'column' => 11]] 'locations' => [['line' => 4, 'column' => 11]],
] ],
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
);
} }
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() : void public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() : void
@ -506,15 +528,13 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['syncNest' => null],
'syncNest' => null,
],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
'locations' => [['line' => 4, 'column' => 11]] 'locations' => [['line' => 4, 'column' => 11]],
],
], ],
]
]; ];
$this->assertArraySubset( $this->assertArraySubset(
@ -536,15 +556,13 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promiseNest' => null],
'promiseNest' => null,
],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
'locations' => [['line' => 4, 'column' => 11]] 'locations' => [['line' => 4, 'column' => 11]],
],
], ],
]
]; ];
$this->assertArraySubset( $this->assertArraySubset(
@ -566,15 +584,13 @@ class NonNullTest extends TestCase
$ast = Parser::parse($doc); $ast = Parser::parse($doc);
$expected = [ $expected = [
'data' => [ 'data' => ['promiseNest' => null],
'promiseNest' => null,
],
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
'locations' => [['line' => 4, 'column' => 11]] 'locations' => [['line' => 4, 'column' => 11]],
],
], ],
]
]; ];
$this->assertArraySubset( $this->assertArraySubset(
@ -628,7 +644,7 @@ class NonNullTest extends TestCase
'promiseNest' => [ 'promiseNest' => [
'sync' => null, 'sync' => null,
'promise' => null, 'promise' => null,
] ],
], ],
'promiseNest' => [ 'promiseNest' => [
'sync' => null, 'sync' => null,
@ -640,9 +656,9 @@ class NonNullTest extends TestCase
'promiseNest' => [ 'promiseNest' => [
'sync' => null, 'sync' => null,
'promise' => null, 'promise' => null,
] ],
] ],
] ],
]; ];
$actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(); $actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray();
@ -714,7 +730,7 @@ class NonNullTest extends TestCase
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 19, 'column' => 19]]], ['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 19, 'column' => 19]]],
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 30, 'column' => 19]]], ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 30, 'column' => 19]]],
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 41, 'column' => 19]]], ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 41, 'column' => 19]]],
] ],
]; ];
$this->assertArraySubset( $this->assertArraySubset(
@ -734,8 +750,8 @@ class NonNullTest extends TestCase
$expected = [ $expected = [
'errors' => [ 'errors' => [
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)]) FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)]),
] ],
]; ];
$actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray(); $actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray();
$this->assertArraySubset($expected, $actual); $this->assertArraySubset($expected, $actual);
@ -752,10 +768,13 @@ class NonNullTest extends TestCase
$expected = [ $expected = [
'errors' => [ 'errors' => [
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(2, 17)]), FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(2, 17)]),
] ],
]; ];
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); $this->assertArraySubset(
$expected,
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
);
} }
public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() : void public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() : void
@ -769,9 +788,9 @@ class NonNullTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
'locations' => [['line' => 2, 'column' => 17]] 'locations' => [['line' => 2, 'column' => 17]],
],
], ],
]
]; ];
$this->assertArraySubset( $this->assertArraySubset(
$expected, $expected,
@ -791,9 +810,9 @@ class NonNullTest extends TestCase
'errors' => [ 'errors' => [
[ [
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
'locations' => [['line' => 2, 'column' => 17]] 'locations' => [['line' => 2, 'column' => 17]],
],
], ],
]
]; ];
$this->assertArraySubset( $this->assertArraySubset(

View File

@ -1,17 +1,17 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\Promise; namespace GraphQL\Tests\Executor\Promise;
use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter; use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter;
use GraphQL\Executor\Promise\Promise;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use React\Promise\Deferred; use React\Promise\Deferred;
use React\Promise\FulfilledPromise; use React\Promise\FulfilledPromise;
use React\Promise\LazyPromise; use React\Promise\LazyPromise;
use React\Promise\Promise as ReactPromise; use React\Promise\Promise as ReactPromise;
use React\Promise\RejectedPromise; use React\Promise\RejectedPromise;
use function class_exists;
/** /**
* @group ReactPromise * @group ReactPromise
@ -20,19 +20,29 @@ class ReactPromiseAdapterTest extends TestCase
{ {
public function setUp() public function setUp()
{ {
if(! class_exists('React\Promise\Promise')) { if (class_exists('React\Promise\Promise')) {
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest'); return;
} }
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
} }
public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void
{ {
$reactAdapter = new ReactPromiseAdapter(); $reactAdapter = new ReactPromiseAdapter();
$this->assertSame(true, $reactAdapter->isThenable(new ReactPromise(function() {}))); $this->assertSame(
true,
$reactAdapter->isThenable(new ReactPromise(function () {
}))
);
$this->assertSame(true, $reactAdapter->isThenable(new FulfilledPromise())); $this->assertSame(true, $reactAdapter->isThenable(new FulfilledPromise()));
$this->assertSame(true, $reactAdapter->isThenable(new RejectedPromise())); $this->assertSame(true, $reactAdapter->isThenable(new RejectedPromise()));
$this->assertSame(true, $reactAdapter->isThenable(new LazyPromise(function() {}))); $this->assertSame(
true,
$reactAdapter->isThenable(new LazyPromise(function () {
}))
);
$this->assertSame(false, $reactAdapter->isThenable(false)); $this->assertSame(false, $reactAdapter->isThenable(false));
$this->assertSame(false, $reactAdapter->isThenable(true)); $this->assertSame(false, $reactAdapter->isThenable(true));
$this->assertSame(false, $reactAdapter->isThenable(1)); $this->assertSame(false, $reactAdapter->isThenable(1));
@ -62,9 +72,12 @@ class ReactPromiseAdapterTest extends TestCase
$result = null; $result = null;
$resultPromise = $reactAdapter->then($promise, function ($value) use (&$result) { $resultPromise = $reactAdapter->then(
$promise,
function ($value) use (&$result) {
$result = $value; $result = $value;
}); }
);
$this->assertSame(1, $result); $this->assertSame(1, $result);
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise);
@ -117,9 +130,12 @@ class ReactPromiseAdapterTest extends TestCase
$exception = null; $exception = null;
$rejectedPromise->then(null, function ($error) use (&$exception) { $rejectedPromise->then(
null,
function ($error) use (&$exception) {
$exception = $error; $exception = $error;
}); }
);
$this->assertInstanceOf('\Exception', $exception); $this->assertInstanceOf('\Exception', $exception);
$this->assertEquals('I am a bad promise', $exception->getMessage()); $this->assertEquals('I am a bad promise', $exception->getMessage());

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\Promise; namespace GraphQL\Tests\Executor\Promise;
use GraphQL\Deferred; use GraphQL\Deferred;
@ -10,9 +13,7 @@ use PHPUnit\Framework\TestCase;
class SyncPromiseAdapterTest extends TestCase class SyncPromiseAdapterTest extends TestCase
{ {
/** /** @var SyncPromiseAdapter */
* @var SyncPromiseAdapter
*/
private $promises; private $promises;
public function setUp() public function setUp()
@ -22,7 +23,11 @@ class SyncPromiseAdapterTest extends TestCase
public function testIsThenable() : void public function testIsThenable() : void
{ {
$this->assertEquals(true, $this->promises->isThenable(new Deferred(function() {}))); $this->assertEquals(
true,
$this->promises->isThenable(new Deferred(function () {
}))
);
$this->assertEquals(false, $this->promises->isThenable(false)); $this->assertEquals(false, $this->promises->isThenable(false));
$this->assertEquals(false, $this->promises->isThenable(true)); $this->assertEquals(false, $this->promises->isThenable(true));
$this->assertEquals(false, $this->promises->isThenable(1)); $this->assertEquals(false, $this->promises->isThenable(1));
@ -35,7 +40,8 @@ class SyncPromiseAdapterTest extends TestCase
public function testConvert() : void public function testConvert() : void
{ {
$dfd = new Deferred(function() {}); $dfd = new Deferred(function () {
});
$result = $this->promises->convertThenable($dfd); $result = $this->promises->convertThenable($dfd);
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result);
@ -48,7 +54,8 @@ class SyncPromiseAdapterTest extends TestCase
public function testThen() : void public function testThen() : void
{ {
$dfd = new Deferred(function() {}); $dfd = new Deferred(function () {
});
$promise = $this->promises->convertThenable($dfd); $promise = $this->promises->convertThenable($dfd);
$result = $this->promises->then($promise); $result = $this->promises->then($promise);
@ -59,7 +66,8 @@ class SyncPromiseAdapterTest extends TestCase
public function testCreatePromise() : void public function testCreatePromise() : void
{ {
$promise = $this->promises->create(function($resolve, $reject) {}); $promise = $this->promises->create(function ($resolve, $reject) {
});
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise); $this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
@ -71,6 +79,42 @@ class SyncPromiseAdapterTest extends TestCase
$this->assertValidPromise($promise, null, 'A', SyncPromise::FULFILLED); $this->assertValidPromise($promise, null, 'A', SyncPromise::FULFILLED);
} }
private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
{
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
$actualNextValue = null;
$actualNextReason = null;
$onFulfilledCalled = false;
$onRejectedCalled = false;
$promise->then(
function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
$onFulfilledCalled = true;
$actualNextValue = $nextValue;
},
function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) {
$onRejectedCalled = true;
$actualNextReason = $reason->getMessage();
}
);
$this->assertSame($onFulfilledCalled, false);
$this->assertSame($onRejectedCalled, false);
SyncPromise::runQueue();
if ($expectedNextState !== SyncPromise::PENDING) {
$this->assertSame(! $expectedNextReason, $onFulfilledCalled);
$this->assertSame(! ! $expectedNextReason, $onRejectedCalled);
}
$this->assertSame($expectedNextValue, $actualNextValue);
$this->assertSame($expectedNextReason, $actualNextReason);
$this->assertSame($expectedNextState, $promise->adoptedPromise->state);
}
public function testCreateFulfilledPromise() : void public function testCreateFulfilledPromise() : void
{ {
$promise = $this->promises->createFulfilled('test'); $promise = $this->promises->createFulfilled('test');
@ -105,7 +149,7 @@ class SyncPromiseAdapterTest extends TestCase
new Promise($promise2, $this->promises), new Promise($promise2, $this->promises),
3, 3,
new Promise($promise3, $this->promises), new Promise($promise3, $this->promises),
[] [],
]; ];
$promise = $this->promises->all($data); $promise = $this->promises->all($data);
@ -114,7 +158,12 @@ class SyncPromiseAdapterTest extends TestCase
$promise1->resolve('value1'); $promise1->resolve('value1');
$this->assertValidPromise($promise, null, null, SyncPromise::PENDING); $this->assertValidPromise($promise, null, null, SyncPromise::PENDING);
$promise2->resolve('value2'); $promise2->resolve('value2');
$this->assertValidPromise($promise, null, ['1', 'value1', 'value2', 3, 'value2-value3', []], SyncPromise::FULFILLED); $this->assertValidPromise(
$promise,
null,
['1', 'value1', 'value2', 3, 'value2-value3', []],
SyncPromise::FULFILLED
);
} }
public function testWait() : void public function testWait() : void
@ -123,10 +172,12 @@ class SyncPromiseAdapterTest extends TestCase
$deferred1 = new Deferred(function () use (&$called) { $deferred1 = new Deferred(function () use (&$called) {
$called[] = 1; $called[] = 1;
return 1; return 1;
}); });
$deferred2 = new Deferred(function () use (&$called) { $deferred2 = new Deferred(function () use (&$called) {
$called[] = 2; $called[] = 2;
return 2; return 2;
}); });
@ -136,14 +187,17 @@ class SyncPromiseAdapterTest extends TestCase
$p3 = $p2->then(function () use (&$called) { $p3 = $p2->then(function () use (&$called) {
$dfd = new Deferred(function () use (&$called) { $dfd = new Deferred(function () use (&$called) {
$called[] = 3; $called[] = 3;
return 3; return 3;
}); });
return $this->promises->convertThenable($dfd); return $this->promises->convertThenable($dfd);
}); });
$p4 = $p3->then(function () use (&$called) { $p4 = $p3->then(function () use (&$called) {
return new Deferred(function () use (&$called) { return new Deferred(function () use (&$called) {
$called[] = 4; $called[] = 4;
return 4; return 4;
}); });
}); });
@ -162,40 +216,4 @@ class SyncPromiseAdapterTest extends TestCase
$this->assertEquals([1, 2, 3, 4], $called); $this->assertEquals([1, 2, 3, 4], $called);
$this->assertValidPromise($all, null, [0, 1, 2, 3, 4], SyncPromise::FULFILLED); $this->assertValidPromise($all, null, [0, 1, 2, 3, 4], SyncPromise::FULFILLED);
} }
private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
{
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
$actualNextValue = null;
$actualNextReason = null;
$onFulfilledCalled = false;
$onRejectedCalled = false;
$promise->then(
function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
$onFulfilledCalled = true;
$actualNextValue = $nextValue;
},
function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) {
$onRejectedCalled = true;
$actualNextReason = $reason->getMessage();
}
);
$this->assertSame($onFulfilledCalled, false);
$this->assertSame($onRejectedCalled, false);
SyncPromise::runQueue();
if ($expectedNextState !== SyncPromise::PENDING) {
$this->assertSame(!$expectedNextReason, $onFulfilledCalled);
$this->assertSame(!!$expectedNextReason, $onRejectedCalled);
}
$this->assertSame($expectedNextValue, $actualNextValue);
$this->assertSame($expectedNextReason, $actualNextReason);
$this->assertSame($expectedNextState, $promise->adoptedPromise->state);
}
} }

View File

@ -1,9 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\Promise; namespace GraphQL\Tests\Executor\Promise;
use GraphQL\Executor\Promise\Adapter\SyncPromise; use GraphQL\Executor\Promise\Adapter\SyncPromise;
use PHPUnit\Framework\Error\Error; use PHPUnit\Framework\Error\Error;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function uniqid;
class SyncPromiseTest extends TestCase class SyncPromiseTest extends TestCase
{ {
@ -12,14 +16,17 @@ class SyncPromiseTest extends TestCase
$onFulfilledReturnsNull = function () { $onFulfilledReturnsNull = function () {
return null; return null;
}; };
$onFulfilledReturnsSameValue = function ($value) { $onFulfilledReturnsSameValue = function ($value) {
return $value; return $value;
}; };
$onFulfilledReturnsOtherValue = function ($value) { $onFulfilledReturnsOtherValue = function ($value) {
return 'other-' . $value; return 'other-' . $value;
}; };
$onFulfilledThrows = function ($value) { $onFulfilledThrows = function ($value) {
throw new \Exception("onFulfilled throws this!"); throw new \Exception('onFulfilled throws this!');
}; };
return [ return [
@ -28,7 +35,7 @@ class SyncPromiseTest extends TestCase
[uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED], [uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED],
['test-value', $onFulfilledReturnsSameValue, 'test-value', null, SyncPromise::FULFILLED], ['test-value', $onFulfilledReturnsSameValue, 'test-value', null, SyncPromise::FULFILLED],
['test-value-2', $onFulfilledReturnsOtherValue, 'other-test-value-2', null, SyncPromise::FULFILLED], ['test-value-2', $onFulfilledReturnsOtherValue, 'other-test-value-2', null, SyncPromise::FULFILLED],
['test-value-3', $onFulfilledThrows, null, "onFulfilled throws this!", SyncPromise::REJECTED], ['test-value-3', $onFulfilledThrows, null, 'onFulfilled throws this!', SyncPromise::REJECTED],
]; ];
} }
@ -41,8 +48,7 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
@ -63,8 +69,7 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
@ -85,21 +90,27 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
$promise->resolve($resolvedValue); $promise->resolve($resolvedValue);
$this->assertEquals(SyncPromise::FULFILLED, $promise->state); $this->assertEquals(SyncPromise::FULFILLED, $promise->state);
$nextPromise = $promise->then(null, function() {}); $nextPromise = $promise->then(
null,
function () {
}
);
$this->assertSame($promise, $nextPromise); $this->assertSame($promise, $nextPromise);
$onRejectedCalled = false; $onRejectedCalled = false;
$nextPromise = $promise->then($onFulfilled, function () use (&$onRejectedCalled) { $nextPromise = $promise->then(
$onFulfilled,
function () use (&$onRejectedCalled) {
$onRejectedCalled = true; $onRejectedCalled = true;
}); }
);
if ($onFulfilled) { if ($onFulfilled) {
$this->assertNotSame($promise, $nextPromise); $this->assertNotSame($promise, $nextPromise);
@ -124,19 +135,57 @@ class SyncPromiseTest extends TestCase
$this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState); $this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState);
} }
private function assertValidPromise(
SyncPromise $promise,
$expectedNextReason,
$expectedNextValue,
$expectedNextState
) {
$actualNextValue = null;
$actualNextReason = null;
$onFulfilledCalled = false;
$onRejectedCalled = false;
$promise->then(
function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
$onFulfilledCalled = true;
$actualNextValue = $nextValue;
},
function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) {
$onRejectedCalled = true;
$actualNextReason = $reason->getMessage();
}
);
$this->assertEquals($onFulfilledCalled, false);
$this->assertEquals($onRejectedCalled, false);
SyncPromise::runQueue();
$this->assertEquals(! $expectedNextReason, $onFulfilledCalled);
$this->assertEquals(! ! $expectedNextReason, $onRejectedCalled);
$this->assertEquals($expectedNextValue, $actualNextValue);
$this->assertEquals($expectedNextReason, $actualNextReason);
$this->assertEquals($expectedNextState, $promise->state);
}
public function getRejectedPromiseData() public function getRejectedPromiseData()
{ {
$onRejectedReturnsNull = function () { $onRejectedReturnsNull = function () {
return null; return null;
}; };
$onRejectedReturnsSomeValue = function ($reason) { $onRejectedReturnsSomeValue = function ($reason) {
return 'some-value'; return 'some-value';
}; };
$onRejectedThrowsSameReason = function ($reason) { $onRejectedThrowsSameReason = function ($reason) {
throw $reason; throw $reason;
}; };
$onRejectedThrowsOtherReason = function ($value) { $onRejectedThrowsOtherReason = function ($value) {
throw new \Exception("onRejected throws other!"); throw new \Exception('onRejected throws other!');
}; };
return [ return [
@ -158,8 +207,7 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
@ -169,7 +217,6 @@ class SyncPromiseTest extends TestCase
$this->expectException(\Throwable::class); $this->expectException(\Throwable::class);
$this->expectExceptionMessage('Cannot change rejection reason'); $this->expectExceptionMessage('Cannot change rejection reason');
$promise->reject(new \Exception('other-reason')); $promise->reject(new \Exception('other-reason'));
} }
/** /**
@ -181,8 +228,7 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
@ -203,8 +249,7 @@ class SyncPromiseTest extends TestCase
$expectedNextValue, $expectedNextValue,
$expectedNextReason, $expectedNextReason,
$expectedNextState $expectedNextState
) ) {
{
$promise = new SyncPromise(); $promise = new SyncPromise();
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
@ -214,18 +259,22 @@ class SyncPromiseTest extends TestCase
try { try {
$promise->reject(new \Exception('other-reason')); $promise->reject(new \Exception('other-reason'));
$this->fail('Expected exception not thrown'); $this->fail('Expected exception not thrown');
} catch (\Exception $e) { } catch (\Throwable $e) {
$this->assertEquals('Cannot change rejection reason', $e->getMessage()); $this->assertEquals('Cannot change rejection reason', $e->getMessage());
} }
try { try {
$promise->resolve('anything'); $promise->resolve('anything');
$this->fail('Expected exception not thrown'); $this->fail('Expected exception not thrown');
} catch (\Exception $e) { } catch (\Throwable $e) {
$this->assertEquals('Cannot resolve rejected promise', $e->getMessage()); $this->assertEquals('Cannot resolve rejected promise', $e->getMessage());
} }
$nextPromise = $promise->then(function() {}, null); $nextPromise = $promise->then(
function () {
},
null
);
$this->assertSame($promise, $nextPromise); $this->assertSame($promise, $nextPromise);
$onFulfilledCalled = false; $onFulfilledCalled = false;
@ -266,7 +315,7 @@ class SyncPromiseTest extends TestCase
try { try {
$promise->resolve($promise); $promise->resolve($promise);
$this->fail('Expected exception not thrown'); $this->fail('Expected exception not thrown');
} catch (\Exception $e) { } catch (\Throwable $e) {
$this->assertEquals('Cannot resolve promise with self', $e->getMessage()); $this->assertEquals('Cannot resolve promise with self', $e->getMessage());
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
} }
@ -299,24 +348,25 @@ class SyncPromiseTest extends TestCase
throw $e; throw $e;
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->assertEquals(SyncPromise::PENDING, $promise->state); $this->assertEquals(SyncPromise::PENDING, $promise->state);
} catch (\Exception $e) {
$this->assertEquals(SyncPromise::PENDING, $promise->state);
} }
$promise->reject(new \Exception("Rejected Reason")); $promise->reject(new \Exception('Rejected Reason'));
$this->assertValidPromise($promise, "Rejected Reason", null, SyncPromise::REJECTED); $this->assertValidPromise($promise, 'Rejected Reason', null, SyncPromise::REJECTED);
$promise = new SyncPromise(); $promise = new SyncPromise();
$promise2 = $promise->then(null, function() { $promise2 = $promise->then(
null,
function () {
return 'value'; return 'value';
}); }
$promise->reject(new \Exception("Rejected Again")); );
$promise->reject(new \Exception('Rejected Again'));
$this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED); $this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED);
$promise = new SyncPromise(); $promise = new SyncPromise();
$promise2 = $promise->then(); $promise2 = $promise->then();
$promise->reject(new \Exception("Rejected Once Again")); $promise->reject(new \Exception('Rejected Once Again'));
$this->assertValidPromise($promise2, "Rejected Once Again", null, SyncPromise::REJECTED); $this->assertValidPromise($promise2, 'Rejected Once Again', null, SyncPromise::REJECTED);
} }
public function testPendingPromiseThen() : void public function testPendingPromiseThen() : void
@ -334,8 +384,10 @@ class SyncPromiseTest extends TestCase
$onRejectedCount = 0; $onRejectedCount = 0;
$onFulfilled = function ($value) use (&$onFulfilledCount) { $onFulfilled = function ($value) use (&$onFulfilledCount) {
$onFulfilledCount++; $onFulfilledCount++;
return $onFulfilledCount; return $onFulfilledCount;
}; };
$onRejected = function ($reason) use (&$onRejectedCount) { $onRejected = function ($reason) use (&$onRejectedCount) {
$onRejectedCount++; $onRejectedCount++;
throw $reason; throw $reason;
@ -367,35 +419,4 @@ class SyncPromiseTest extends TestCase
$this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED); $this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED);
$this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED); $this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED);
} }
private function assertValidPromise(SyncPromise $promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
{
$actualNextValue = null;
$actualNextReason = null;
$onFulfilledCalled = false;
$onRejectedCalled = false;
$promise->then(
function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
$onFulfilledCalled = true;
$actualNextValue = $nextValue;
},
function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) {
$onRejectedCalled = true;
$actualNextReason = $reason->getMessage();
}
);
$this->assertEquals($onFulfilledCalled, false);
$this->assertEquals($onRejectedCalled, false);
SyncPromise::runQueue();
$this->assertEquals(!$expectedNextReason, $onFulfilledCalled);
$this->assertEquals(!!$expectedNextReason, $onRejectedCalled);
$this->assertEquals($expectedNextValue, $actualNextValue);
$this->assertEquals($expectedNextReason, $actualNextReason);
$this->assertEquals($expectedNextState, $promise->state);
}
} }

View File

@ -1,30 +1,22 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\Adder;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
require_once __DIR__ . '/TestClasses.php';
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function json_encode;
use function uniqid;
class ResolveTest extends TestCase class ResolveTest extends TestCase
{ {
// Execute: resolve function // Execute: resolve function
private function buildSchema($testField)
{
return new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'test' => $testField
]
])
]);
}
/** /**
* @see it('default function accesses properties') * @see it('default function accesses properties')
*/ */
@ -32,9 +24,7 @@ class ResolveTest extends TestCase
{ {
$schema = $this->buildSchema(['type' => Type::string()]); $schema = $this->buildSchema(['type' => Type::string()]);
$source = [ $source = ['test' => 'testValue'];
'test' => 'testValue'
];
$this->assertEquals( $this->assertEquals(
['data' => ['test' => 'testValue']], ['data' => ['test' => 'testValue']],
@ -42,6 +32,16 @@ class ResolveTest extends TestCase
); );
} }
private function buildSchema($testField)
{
return new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => ['test' => $testField],
]),
]);
}
/** /**
* @see it('default function calls methods') * @see it('default function calls methods')
*/ */
@ -53,7 +53,7 @@ class ResolveTest extends TestCase
$source = [ $source = [
'test' => function () use ($_secret) { 'test' => function () use ($_secret) {
return $_secret; return $_secret;
} },
]; ];
$this->assertEquals( $this->assertEquals(
['data' => ['test' => $_secret]], ['data' => ['test' => $_secret]],
@ -92,7 +92,7 @@ class ResolveTest extends TestCase
], ],
'resolve' => function ($source, $args) { 'resolve' => function ($source, $args) {
return json_encode([$source, $args]); return json_encode([$source, $args]);
} },
]); ]);
$this->assertEquals( $this->assertEquals(

View File

@ -1,8 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Deferred; use GraphQL\Deferred;
use GraphQL\Error\Error;
use GraphQL\Error\FormattedError; use GraphQL\Error\FormattedError;
use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
@ -36,7 +38,7 @@ class SyncTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($rootValue) { 'resolve' => function ($rootValue) {
return $rootValue; return $rootValue;
} },
], ],
'asyncField' => [ 'asyncField' => [
'type' => Type::string(), 'type' => Type::string(),
@ -44,9 +46,9 @@ class SyncTest extends TestCase
return new Deferred(function () use ($rootValue) { return new Deferred(function () use ($rootValue) {
return $rootValue; return $rootValue;
}); });
} },
] ],
] ],
]), ]),
'mutation' => new ObjectType([ 'mutation' => new ObjectType([
'name' => 'Mutation', 'name' => 'Mutation',
@ -55,11 +57,12 @@ class SyncTest extends TestCase
'type' => Type::string(), 'type' => Type::string(),
'resolve' => function ($rootValue) { 'resolve' => function ($rootValue) {
return $rootValue; return $rootValue;
} },
] ],
] ],
]) ]),
]); ]);
$this->promiseAdapter = new SyncPromiseAdapter(); $this->promiseAdapter = new SyncPromiseAdapter();
} }
@ -79,6 +82,26 @@ class SyncTest extends TestCase
$this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result); $this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result);
} }
private function execute($schema, $doc, $rootValue = null)
{
return Executor::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
}
private function assertSync($expectedFinalArray, $actualResult)
{
$message = 'Failed assertion that execution was synchronous';
$this->assertInstanceOf(Promise::class, $actualResult, $message);
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
$this->assertEquals(SyncPromise::FULFILLED, $actualResult->adoptedPromise->state, $message);
$this->assertInstanceOf(ExecutionResult::class, $actualResult->adoptedPromise->result, $message);
$this->assertArraySubset(
$expectedFinalArray,
$actualResult->adoptedPromise->result->toArray(),
false,
$message
);
}
/** /**
* @see it('does not return a Promise if fields are all synchronous') * @see it('does not return a Promise if fields are all synchronous')
*/ */
@ -93,6 +116,8 @@ class SyncTest extends TestCase
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result); $this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
} }
// Describe: graphqlSync
/** /**
* @see it('does not return a Promise if mutation fields are all synchronous') * @see it('does not return a Promise if mutation fields are all synchronous')
*/ */
@ -121,7 +146,16 @@ class SyncTest extends TestCase
$this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result); $this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result);
} }
// Describe: graphqlSync private function assertAsync($expectedFinalArray, $actualResult)
{
$message = 'Failed assertion that execution was asynchronous';
$this->assertInstanceOf(Promise::class, $actualResult, $message);
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
$this->assertEquals(SyncPromise::PENDING, $actualResult->adoptedPromise->state, $message);
$resolvedResult = $this->promiseAdapter->wait($actualResult);
$this->assertInstanceOf(ExecutionResult::class, $resolvedResult, $message);
$this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), false, $message);
}
/** /**
* @see it('does not return a Promise for syntax errors') * @see it('does not return a Promise for syntax errors')
@ -133,12 +167,22 @@ class SyncTest extends TestCase
$this->schema, $this->schema,
$doc $doc
); );
$this->assertSync([ $this->assertSync(
[
'errors' => [ 'errors' => [
['message' => 'Syntax Error: Expected Name, found {', [
'locations' => [['line' => 1, 'column' => 29]]] 'message' => 'Syntax Error: Expected Name, found {',
] 'locations' => [['line' => 1, 'column' => 29]],
], $result); ],
],
],
$result
);
}
private function graphqlSync($schema, $doc, $rootValue = null)
{
return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
} }
/** /**
@ -153,9 +197,12 @@ class SyncTest extends TestCase
$doc $doc
); );
$expected = [ $expected = [
'errors' => Utils::map($validationErrors, function ($e) { 'errors' => Utils::map(
$validationErrors,
function ($e) {
return FormattedError::createFromException($e); return FormattedError::createFromException($e);
}) }
),
]; ];
$this->assertSync($expected, $result); $this->assertSync($expected, $result);
} }
@ -173,35 +220,4 @@ class SyncTest extends TestCase
); );
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result); $this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
} }
private function graphqlSync($schema, $doc, $rootValue = null)
{
return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
}
private function execute($schema, $doc, $rootValue = null)
{
return Executor::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
}
private function assertSync($expectedFinalArray, $actualResult)
{
$message = 'Failed assertion that execution was synchronous';
$this->assertInstanceOf(Promise::class, $actualResult, $message);
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
$this->assertEquals(SyncPromise::FULFILLED, $actualResult->adoptedPromise->state, $message);
$this->assertInstanceOf(ExecutionResult::class, $actualResult->adoptedPromise->result, $message);
$this->assertArraySubset($expectedFinalArray, $actualResult->adoptedPromise->result->toArray(), $message);
}
private function assertAsync($expectedFinalArray, $actualResult)
{
$message = 'Failed assertion that execution was asynchronous';
$this->assertInstanceOf(Promise::class, $actualResult, $message);
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
$this->assertEquals(SyncPromise::PENDING, $actualResult->adoptedPromise->state, $message);
$resolvedResult = $this->promiseAdapter->wait($actualResult);
$this->assertInstanceOf(ExecutionResult::class, $resolvedResult, $message);
$this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), $message);
}
} }

View File

@ -1,128 +0,0 @@
<?php
namespace GraphQL\Tests\Executor;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;
class Dog
{
function __construct($name, $woofs)
{
$this->name = $name;
$this->woofs = $woofs;
}
}
class Cat
{
function __construct($name, $meows)
{
$this->name = $name;
$this->meows = $meows;
}
}
class Human
{
function __construct($name)
{
$this->name = $name;
}
}
class Person
{
public $name;
public $pets;
public $friends;
function __construct($name, $pets = null, $friends = null)
{
$this->name = $name;
$this->pets = $pets;
$this->friends = $friends;
}
}
class ComplexScalar extends ScalarType
{
public static function create()
{
return new self();
}
public $name = 'ComplexScalar';
/**
* {@inheritdoc}
*/
public function serialize($value)
{
if ($value === 'DeserializedValue') {
return 'SerializedValue';
}
throw new Error("Cannot serialize value as ComplexScalar: " . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseValue($value)
{
if ($value === 'SerializedValue') {
return 'DeserializedValue';
}
throw new Error("Cannot represent value as ComplexScalar: " . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseLiteral($valueNode, array $variables = null)
{
if ($valueNode->value === 'SerializedValue') {
return 'DeserializedValue';
}
throw new Error("Cannot represent literal as ComplexScalar: " . Utils::printSafe($valueNode->value));
}
}
class Special
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
class NotSpecial
{
public $value;
public function __construct($value)
{
$this->value = $value;
}
}
class Adder
{
public $num;
public $test;
public function __construct($num)
{
$this->num = $num;
$this->test = function($source, $args, $context) {
return $this->num + $args['addend1'] + $context['addend2'];
};
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Adder
{
/** @var float */
public $num;
/** @var callable */
public $test;
public function __construct(float $num)
{
$this->num = $num;
$this->test = function ($source, $args, $context) {
return $this->num + $args['addend1'] + $context['addend2'];
};
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Cat
{
/** @var string */
public $name;
/** @var bool */
public $meows;
public function __construct(string $name, bool $meows)
{
$this->name = $name;
$this->meows = $meows;
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;
class ComplexScalar extends ScalarType
{
/** @var string */
public $name = 'ComplexScalar';
public static function create() : self
{
return new self();
}
/**
* {@inheritdoc}
*/
public function serialize($value)
{
if ($value === 'DeserializedValue') {
return 'SerializedValue';
}
throw new Error('Cannot serialize value as ComplexScalar: ' . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseValue($value)
{
if ($value === 'SerializedValue') {
return 'DeserializedValue';
}
throw new Error('Cannot represent value as ComplexScalar: ' . Utils::printSafe($value));
}
/**
* {@inheritdoc}
*/
public function parseLiteral($valueNode, ?array $variables = null)
{
if ($valueNode->value === 'SerializedValue') {
return 'DeserializedValue';
}
throw new Error('Cannot represent literal as ComplexScalar: ' . Utils::printSafe($valueNode->value));
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Dog
{
/** @var string */
public $name;
/** @var bool */
public $woofs;
public function __construct(string $name, bool $woofs)
{
$this->name = $name;
$this->woofs = $woofs;
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Human
{
/** @var string */
public $name;
public function __construct(string $name)
{
$this->name = $name;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class NotSpecial
{
/** @var string */
public $value;
/**
* @param string $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class NumberHolder
{
/** @var float */
public $theNumber;
public function __construct(float $originalNumber)
{
$this->theNumber = $originalNumber;
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Person
{
/** @var string */
public $name;
/** @var (Dog|Cat)[]|null */
public $pets;
/** @var (Dog|Cat|Person)[]|null */
public $friends;
/**
* @param (Cat|Dog)[]|null $pets
* @param (Cat|Dog|Person)[]|null $friends
*/
public function __construct(string $name, $pets = null, $friends = null)
{
$this->name = $name;
$this->pets = $pets;
$this->friends = $friends;
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
use GraphQL\Deferred;
class Root
{
/** @var NumberHolder */
public $numberHolder;
public function __construct(float $originalNumber)
{
$this->numberHolder = new NumberHolder($originalNumber);
}
public function promiseToChangeTheNumber($newNumber) : Deferred
{
return new Deferred(function () use ($newNumber) {
return $this->immediatelyChangeTheNumber($newNumber);
});
}
public function immediatelyChangeTheNumber($newNumber) : NumberHolder
{
$this->numberHolder->theNumber = $newNumber;
return $this->numberHolder;
}
public function failToChangeTheNumber() : void
{
throw new \Exception('Cannot change the number');
}
public function promiseAndFailToChangeTheNumber() : Deferred
{
return new Deferred(function () {
$this->failToChangeTheNumber();
});
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor\TestClasses;
class Special
{
/** @var string */
public $value;
/**
* @param string $value
*/
public function __construct($value)
{
$this->value = $value;
}
}

View File

@ -1,26 +1,38 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
require_once __DIR__ . '/TestClasses.php';
use GraphQL\Error\Warning;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\Cat;
use GraphQL\Tests\Executor\TestClasses\Dog;
use GraphQL\Tests\Executor\TestClasses\Person;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class UnionInterfaceTest extends TestCase class UnionInterfaceTest extends TestCase
{ {
/** @var */
public $schema; public $schema;
/** @var Cat */
public $garfield; public $garfield;
/** @var Dog */
public $odie; public $odie;
/** @var Person */
public $liz; public $liz;
/** @var Person */
public $john; public $john;
public function setUp() public function setUp()
@ -28,8 +40,8 @@ class UnionInterfaceTest extends TestCase
$NamedType = new InterfaceType([ $NamedType = new InterfaceType([
'name' => 'Named', 'name' => 'Named',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
] ],
]); ]);
$DogType = new ObjectType([ $DogType = new ObjectType([
@ -37,11 +49,11 @@ class UnionInterfaceTest extends TestCase
'interfaces' => [$NamedType], 'interfaces' => [$NamedType],
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'woofs' => ['type' => Type::boolean()] 'woofs' => ['type' => Type::boolean()],
], ],
'isTypeOf' => function ($value) { 'isTypeOf' => function ($value) {
return $value instanceof Dog; return $value instanceof Dog;
} },
]); ]);
$CatType = new ObjectType([ $CatType = new ObjectType([
@ -49,11 +61,11 @@ class UnionInterfaceTest extends TestCase
'interfaces' => [$NamedType], 'interfaces' => [$NamedType],
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'meows' => ['type' => Type::boolean()] 'meows' => ['type' => Type::boolean()],
], ],
'isTypeOf' => function ($value) { 'isTypeOf' => function ($value) {
return $value instanceof Cat; return $value instanceof Cat;
} },
]); ]);
$PetType = new UnionType([ $PetType = new UnionType([
@ -66,7 +78,7 @@ class UnionInterfaceTest extends TestCase
if ($value instanceof Cat) { if ($value instanceof Cat) {
return $CatType; return $CatType;
} }
} },
]); ]);
$PersonType = new ObjectType([ $PersonType = new ObjectType([
@ -75,23 +87,22 @@ class UnionInterfaceTest extends TestCase
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'pets' => ['type' => Type::listOf($PetType)], 'pets' => ['type' => Type::listOf($PetType)],
'friends' => ['type' => Type::listOf($NamedType)] 'friends' => ['type' => Type::listOf($NamedType)],
], ],
'isTypeOf' => function ($value) { 'isTypeOf' => function ($value) {
return $value instanceof Person; return $value instanceof Person;
} },
]); ]);
$this->schema = new Schema([ $this->schema = new Schema([
'query' => $PersonType, 'query' => $PersonType,
'types' => [ $PetType ] 'types' => [$PetType],
]); ]);
$this->garfield = new Cat('Garfield', false); $this->garfield = new Cat('Garfield', false);
$this->odie = new Dog('Odie', true); $this->odie = new Dog('Odie', true);
$this->liz = new Person('Liz'); $this->liz = new Person('Liz');
$this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]); $this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]);
} }
// Execute: Union and intersection types // Execute: Union and intersection types
@ -101,7 +112,6 @@ class UnionInterfaceTest extends TestCase
*/ */
public function testCanIntrospectOnUnionAndIntersectionTypes() : void public function testCanIntrospectOnUnionAndIntersectionTypes() : void
{ {
$ast = Parser::parse(' $ast = Parser::parse('
{ {
Named: __type(name: "Named") { Named: __type(name: "Named") {
@ -131,16 +141,16 @@ class UnionInterfaceTest extends TestCase
'kind' => 'INTERFACE', 'kind' => 'INTERFACE',
'name' => 'Named', 'name' => 'Named',
'fields' => [ 'fields' => [
['name' => 'name'] ['name' => 'name'],
], ],
'interfaces' => null, 'interfaces' => null,
'possibleTypes' => [ 'possibleTypes' => [
['name' => 'Person'], ['name' => 'Person'],
['name' => 'Dog'], ['name' => 'Dog'],
['name' => 'Cat'] ['name' => 'Cat'],
], ],
'enumValues' => null, 'enumValues' => null,
'inputFields' => null 'inputFields' => null,
], ],
'Pet' => [ 'Pet' => [
'kind' => 'UNION', 'kind' => 'UNION',
@ -149,12 +159,12 @@ class UnionInterfaceTest extends TestCase
'interfaces' => null, 'interfaces' => null,
'possibleTypes' => [ 'possibleTypes' => [
['name' => 'Dog'], ['name' => 'Dog'],
['name' => 'Cat'] ['name' => 'Cat'],
], ],
'enumValues' => null, 'enumValues' => null,
'inputFields' => null 'inputFields' => null,
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray()); $this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray());
} }
@ -183,9 +193,9 @@ class UnionInterfaceTest extends TestCase
'name' => 'John', 'name' => 'John',
'pets' => [ 'pets' => [
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
@ -220,10 +230,10 @@ class UnionInterfaceTest extends TestCase
'name' => 'John', 'name' => 'John',
'pets' => [ 'pets' => [
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
} }
@ -252,9 +262,9 @@ class UnionInterfaceTest extends TestCase
'name' => 'John', 'name' => 'John',
'friends' => [ 'friends' => [
['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Person', 'name' => 'Liz'],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
@ -288,9 +298,9 @@ class UnionInterfaceTest extends TestCase
'name' => 'John', 'name' => 'John',
'friends' => [ 'friends' => [
['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Person', 'name' => 'Liz'],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true)); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true));
@ -339,13 +349,13 @@ class UnionInterfaceTest extends TestCase
'name' => 'John', 'name' => 'John',
'pets' => [ 'pets' => [
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
], ],
'friends' => [ 'friends' => [
['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Person', 'name' => 'Liz'],
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
] ],
] ],
]; ];
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
@ -364,14 +374,25 @@ class UnionInterfaceTest extends TestCase
$NamedType2 = new InterfaceType([ $NamedType2 = new InterfaceType([
'name' => 'Named', 'name' => 'Named',
'fields' => [ 'fields' => [
'name' => ['type' => Type::string()] 'name' => ['type' => Type::string()],
], ],
'resolveType' => function ($obj, $context, ResolveInfo $info) use (&$encounteredContext, &$encounteredSchema, &$encounteredRootValue, &$PersonType2) { 'resolveType' => function (
$obj,
$context,
ResolveInfo $info
) use (
&$encounteredContext,
&
$encounteredSchema,
&$encounteredRootValue,
&$PersonType2
) {
$encounteredContext = $context; $encounteredContext = $context;
$encounteredSchema = $info->schema; $encounteredSchema = $info->schema;
$encounteredRootValue = $info->rootValue; $encounteredRootValue = $info->rootValue;
return $PersonType2; return $PersonType2;
} },
]); ]);
$PersonType2 = new ObjectType([ $PersonType2 = new ObjectType([
@ -383,9 +404,7 @@ class UnionInterfaceTest extends TestCase
], ],
]); ]);
$schema2 = new Schema([ $schema2 = new Schema(['query' => $PersonType2]);
'query' => $PersonType2
]);
$john2 = new Person('John', [], [$this->liz]); $john2 = new Person('John', [], [$this->liz]);

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Executor; namespace GraphQL\Tests\Executor;
use GraphQL\Executor\Values; use GraphQL\Executor\Values;
@ -10,19 +13,97 @@ use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema; use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function count;
use function var_export;
use const PHP_EOL;
class ValuesTest extends TestCase class ValuesTest extends TestCase
{ {
/** @var Schema */
private static $schema;
public function testGetIDVariableValues() : void public function testGetIDVariableValues() : void
{ {
$this->expectInputVariablesMatchOutputVariables(['idInput' => '123456789']); $this->expectInputVariablesMatchOutputVariables(['idInput' => '123456789']);
$this->assertEquals( $this->assertEquals(
['errors' => [], 'coerced' => ['idInput' => '123456789']], ['errors' => [], 'coerced' => ['idInput' => '123456789']],
self::runTestCase(['idInput' => 123456789]), $this->runTestCase(['idInput' => 123456789]),
'Integer ID was not converted to string' 'Integer ID was not converted to string'
); );
} }
private function expectInputVariablesMatchOutputVariables($variables) : void
{
$this->assertEquals(
$variables,
$this->runTestCase($variables)['coerced'],
'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL
);
}
/**
* @param mixed[] $variables
* @return mixed[]
*/
private function runTestCase($variables) : array
{
return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables);
}
private static function getSchema() : Schema
{
if (! self::$schema) {
self::$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'test' => [
'type' => Type::boolean(),
'args' => [
'idInput' => Type::id(),
'boolInput' => Type::boolean(),
'intInput' => Type::int(),
'stringInput' => Type::string(),
'floatInput' => Type::float(),
],
],
],
]),
]);
}
return self::$schema;
}
/**
* @return VariableDefinitionNode[]
*/
private static function getVariableDefinitionNodes() : array
{
$idInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])]),
]);
$boolInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])]),
]);
$intInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])]),
]);
$stringInputDefintion = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])]),
]);
$floatInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])]),
]);
return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition];
}
public function testGetBooleanVariableValues() : void public function testGetBooleanVariableValues() : void
{ {
$this->expectInputVariablesMatchOutputVariables(['boolInput' => true]); $this->expectInputVariablesMatchOutputVariables(['boolInput' => true]);
@ -64,11 +145,20 @@ class ValuesTest extends TestCase
$this->expectGraphQLError(['idInput' => true]); $this->expectGraphQLError(['idInput' => true]);
} }
private function expectGraphQLError($variables) : void
{
$result = $this->runTestCase($variables);
$this->assertGreaterThan(0, count($result['errors']));
}
public function testFloatForIDVariableThrowsError() : void public function testFloatForIDVariableThrowsError() : void
{ {
$this->expectGraphQLError(['idInput' => 1.0]); $this->expectGraphQLError(['idInput' => 1.0]);
} }
/**
* Helpers for running test cases and making assertions
*/
public function testStringForBooleanVariableThrowsError() : void public function testStringForBooleanVariableThrowsError() : void
{ {
$this->expectGraphQLError(['boolInput' => 'true']); $this->expectGraphQLError(['boolInput' => 'true']);
@ -98,77 +188,4 @@ class ValuesTest extends TestCase
{ {
$this->expectGraphQLError(['intInput' => -2147483649]); $this->expectGraphQLError(['intInput' => -2147483649]);
} }
// Helpers for running test cases and making assertions
private function expectInputVariablesMatchOutputVariables($variables)
{
$this->assertEquals(
$variables,
self::runTestCase($variables)['coerced'],
'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL
);
}
private function expectGraphQLError($variables)
{
$result = self::runTestCase($variables);
$this->assertGreaterThan(0, count($result['errors']));
}
private static $schema;
private static function getSchema()
{
if (!self::$schema) {
self::$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'test' => [
'type' => Type::boolean(),
'args' => [
'idInput' => Type::id(),
'boolInput' => Type::boolean(),
'intInput' => Type::int(),
'stringInput' => Type::string(),
'floatInput' => Type::float()
]
],
]
])
]);
}
return self::$schema;
}
private static function getVariableDefinitionNodes()
{
$idInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])])
]);
$boolInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])])
]);
$intInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])])
]);
$stringInputDefintion = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])])
]);
$floatInputDefinition = new VariableDefinitionNode([
'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]),
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])])
]);
return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition];
}
private function runTestCase($variables)
{
return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables);
}
} }

View File

@ -1,16 +1,19 @@
<?php <?php
namespace GraphQL\Tests\Executor;
require_once __DIR__ . '/TestClasses.php'; declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Error\Error; use GraphQL\Error\Error;
use GraphQL\Executor\Executor; use GraphQL\Executor\Executor;
use GraphQL\Language\Parser; use GraphQL\Language\Parser;
use GraphQL\Type\Schema; use GraphQL\Tests\Executor\TestClasses\ComplexScalar;
use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function json_encode;
/** /**
* Execute: Handles inputs * Execute: Handles inputs
@ -18,7 +21,6 @@ use PHPUnit\Framework\TestCase;
*/ */
class VariablesTest extends TestCase class VariablesTest extends TestCase
{ {
public function testUsingInlineStructs() : void public function testUsingInlineStructs() : void
{ {
// executes with complex input: // executes with complex input:
@ -29,9 +31,7 @@ class VariablesTest extends TestCase
'); ');
$expected = [ $expected = [
'data' => [ 'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'
]
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -77,8 +77,9 @@ class VariablesTest extends TestCase
'errors' => [[ 'errors' => [[
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].', 'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
'path' => ['fieldWithObjectInput'], 'path' => ['fieldWithObjectInput'],
'locations' => [['line' => 3, 'column' => 39]] 'locations' => [['line' => 3, 'column' => 39]],
]] ],
],
]; ];
$this->assertArraySubset($expected, $result->toArray()); $this->assertArraySubset($expected, $result->toArray());
@ -94,6 +95,77 @@ class VariablesTest extends TestCase
); );
} }
private function executeQuery($query, $variableValues = null)
{
$document = Parser::parse($query);
return Executor::execute($this->schema(), $document, null, null, $variableValues);
}
/**
* Describe: Handles nullable scalars
*/
public function schema() : Schema
{
$ComplexScalarType = ComplexScalar::create();
$TestInputObject = new InputObjectType([
'name' => 'TestInputObject',
'fields' => [
'a' => ['type' => Type::string()],
'b' => ['type' => Type::listOf(Type::string())],
'c' => ['type' => Type::nonNull(Type::string())],
'd' => ['type' => $ComplexScalarType],
],
]);
$TestNestedInputObject = new InputObjectType([
'name' => 'TestNestedInputObject',
'fields' => [
'na' => ['type' => Type::nonNull($TestInputObject)],
'nb' => ['type' => Type::nonNull(Type::string())],
],
]);
$TestType = new ObjectType([
'name' => 'TestType',
'fields' => [
'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]),
'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]),
'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]),
'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([
'type' => Type::string(),
'defaultValue' => 'Hello World',
]),
'fieldWithNestedInputObject' => $this->fieldWithInputArg([
'type' => $TestNestedInputObject,
'defaultValue' => 'Hello World',
]),
'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]),
'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]),
'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]),
'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]),
],
]);
return new Schema(['query' => $TestType]);
}
private function fieldWithInputArg($inputArg)
{
return [
'type' => Type::string(),
'args' => ['input' => $inputArg],
'resolve' => function ($_, $args) {
if (isset($args['input'])) {
return json_encode($args['input']);
}
return null;
},
];
}
public function testUsingVariables() : void public function testUsingVariables() : void
{ {
$doc = ' $doc = '
@ -119,7 +191,7 @@ class VariablesTest extends TestCase
'); ');
$expected = [ $expected = [
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'] 'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -135,9 +207,7 @@ class VariablesTest extends TestCase
$params = ['input' => ['c' => 'foo', 'd' => 'SerializedValue']]; $params = ['input' => ['c' => 'foo', 'd' => 'SerializedValue']];
$result = $this->executeQuery($doc, $params); $result = $this->executeQuery($doc, $params);
$expected = [ $expected = [
'data' => [ 'data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'],
'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'
]
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -152,9 +222,9 @@ class VariablesTest extends TestCase
'{"a":"foo","b":"bar","c":null}; ' . '{"a":"foo","b":"bar","c":null}; ' .
'Expected non-nullable type String! not to be null at value.c.', 'Expected non-nullable type String! not to be null at value.c.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql' 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -170,8 +240,8 @@ class VariablesTest extends TestCase
'Expected type TestInputObject to be an object.', 'Expected type TestInputObject to be an object.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -187,8 +257,8 @@ class VariablesTest extends TestCase
'Field value.c of required type String! was not provided.', 'Field value.c of required type String! was not provided.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -216,12 +286,11 @@ class VariablesTest extends TestCase
'Field value.nb of required type String! was not provided.', 'Field value.nb of required type String! was not provided.',
'locations' => [['line' => 2, 'column' => 19]], 'locations' => [['line' => 2, 'column' => 19]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
// errors on addition of unknown input field // errors on addition of unknown input field
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog']]; $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog']];
$result = $this->executeQuery($doc, $params); $result = $this->executeQuery($doc, $params);
@ -234,14 +303,12 @@ class VariablesTest extends TestCase
'Field "extra" is not defined by type TestInputObject.', 'Field "extra" is not defined by type TestInputObject.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Handles nullable scalars
/** /**
* @see it('allows nullable inputs to be omitted') * @see it('allows nullable inputs to be omitted')
*/ */
@ -253,7 +320,7 @@ class VariablesTest extends TestCase
} }
'); ');
$expected = [ $expected = [
'data' => ['fieldWithNullableStringInput' => null] 'data' => ['fieldWithNullableStringInput' => null],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
@ -288,6 +355,9 @@ class VariablesTest extends TestCase
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Handles non-nullable scalars
/** /**
* @see it('allows nullable inputs to be set to null in a variable') * @see it('allows nullable inputs to be set to null in a variable')
*/ */
@ -332,9 +402,6 @@ class VariablesTest extends TestCase
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Handles non-nullable scalars
/** /**
* @see it('allows non-nullable inputs to be omitted given a default') * @see it('allows non-nullable inputs to be omitted given a default')
*/ */
@ -346,7 +413,7 @@ class VariablesTest extends TestCase
} }
'); ');
$expected = [ $expected = [
'data' => ['fieldWithNonNullableStringInput' => '"default"'] 'data' => ['fieldWithNonNullableStringInput' => '"default"'],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -367,9 +434,9 @@ class VariablesTest extends TestCase
[ [
'message' => 'Variable "$value" of required type "String!" was not provided.', 'message' => 'Variable "$value" of required type "String!" was not provided.',
'locations' => [['line' => 2, 'column' => 31]], 'locations' => [['line' => 2, 'column' => 31]],
'category' => 'graphql' 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -393,8 +460,8 @@ class VariablesTest extends TestCase
'Expected non-nullable type String! not to be null.', 'Expected non-nullable type String! not to be null.',
'locations' => [['line' => 2, 'column' => 31]], 'locations' => [['line' => 2, 'column' => 31]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -445,17 +512,19 @@ class VariablesTest extends TestCase
'locations' => [['line' => 3, 'column' => 9]], 'locations' => [['line' => 3, 'column' => 9]],
'path' => ['fieldWithNonNullableStringInput'], 'path' => ['fieldWithNonNullableStringInput'],
'category' => 'graphql', 'category' => 'graphql',
]] ],
],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Handles lists and nullability
/** /**
* @see it('reports error for array passed into string input') * @see it('reports error for array passed into string input')
*/ */
public function testReportsErrorForArrayPassedIntoStringInput() : void public function testReportsErrorForArrayPassedIntoStringInput() : void
{ {
$doc = ' $doc = '
query SetsNonNullable($value: String!) { query SetsNonNullable($value: String!) {
fieldWithNonNullableStringInput(input: $value) fieldWithNonNullableStringInput(input: $value)
@ -471,9 +540,10 @@ class VariablesTest extends TestCase
'String; String cannot represent an array value: [1,2,3]', 'String; String cannot represent an array value: [1,2,3]',
'category' => 'graphql', 'category' => 'graphql',
'locations' => [ 'locations' => [
['line' => 2, 'column' => 31] ['line' => 2, 'column' => 31],
] ],
]] ],
],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -512,13 +582,12 @@ class VariablesTest extends TestCase
'locations' => [['line' => 3, 'column' => 48]], 'locations' => [['line' => 3, 'column' => 48]],
'path' => ['fieldWithNonNullableStringInput'], 'path' => ['fieldWithNonNullableStringInput'],
'category' => 'graphql', 'category' => 'graphql',
]] ],
],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Handles lists and nullability
/** /**
* @see it('allows lists to be null') * @see it('allows lists to be null')
*/ */
@ -584,8 +653,8 @@ class VariablesTest extends TestCase
'Expected non-nullable type [String]! not to be null.', 'Expected non-nullable type [String]! not to be null.',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -669,8 +738,8 @@ class VariablesTest extends TestCase
'Expected non-nullable type String! not to be null at value[1].', 'Expected non-nullable type String! not to be null at value[1].',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -694,8 +763,8 @@ class VariablesTest extends TestCase
'Expected non-nullable type [String!]! not to be null.', 'Expected non-nullable type [String!]! not to be null.',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -715,6 +784,8 @@ class VariablesTest extends TestCase
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Execute: Uses argument default values
/** /**
* @see it('does not allow non-null lists of non-nulls to contain null') * @see it('does not allow non-null lists of non-nulls to contain null')
*/ */
@ -734,8 +805,8 @@ class VariablesTest extends TestCase
'Expected non-nullable type String! not to be null at value[1].', 'Expected non-nullable type String! not to be null at value[1].',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -760,8 +831,8 @@ class VariablesTest extends TestCase
'be used as an input type.', 'be used as an input type.',
'locations' => [['line' => 2, 'column' => 25]], 'locations' => [['line' => 2, 'column' => 25]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
@ -787,13 +858,12 @@ class VariablesTest extends TestCase
'cannot be used as an input type.', 'cannot be used as an input type.',
'locations' => [['line' => 2, 'column' => 25]], 'locations' => [['line' => 2, 'column' => 25]],
'category' => 'graphql', 'category' => 'graphql',
] ],
] ],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
// Describe: Execute: Uses argument default values
/** /**
* @see it('when no argument provided') * @see it('when no argument provided')
*/ */
@ -841,77 +911,10 @@ class VariablesTest extends TestCase
'locations' => [['line' => 2, 'column' => 50]], 'locations' => [['line' => 2, 'column' => 50]],
'path' => ['fieldWithDefaultArgumentValue'], 'path' => ['fieldWithDefaultArgumentValue'],
'category' => 'graphql', 'category' => 'graphql',
]] ],
],
]; ];
$this->assertEquals($expected, $result->toArray()); $this->assertEquals($expected, $result->toArray());
} }
public function schema()
{
$ComplexScalarType = ComplexScalar::create();
$TestInputObject = new InputObjectType([
'name' => 'TestInputObject',
'fields' => [
'a' => ['type' => Type::string()],
'b' => ['type' => Type::listOf(Type::string())],
'c' => ['type' => Type::nonNull(Type::string())],
'd' => ['type' => $ComplexScalarType],
]
]);
$TestNestedInputObject = new InputObjectType([
'name' => 'TestNestedInputObject',
'fields' => [
'na' => [ 'type' => Type::nonNull($TestInputObject) ],
'nb' => [ 'type' => Type::nonNull(Type::string()) ],
],
]);
$TestType = new ObjectType([
'name' => 'TestType',
'fields' => [
'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]),
'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]),
'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]),
'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([
'type' => Type::string(),
'defaultValue' => 'Hello World',
]),
'fieldWithNestedInputObject' => $this->fieldWithInputArg([
'type' => $TestNestedInputObject,
'defaultValue' => 'Hello World'
]),
'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]),
'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]),
'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]),
'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]),
]
]);
$schema = new Schema(['query' => $TestType]);
return $schema;
}
private function fieldWithInputArg($inputArg)
{
return [
'type' => Type::string(),
'args' => ['input' => $inputArg],
'resolve' => function ($_, $args) {
if (isset($args['input'])) {
return json_encode($args['input']);
}
return null;
},
];
}
private function executeQuery($query, $variableValues = null)
{
$document = Parser::parse($query);
return Executor::execute($this->schema(), $document, null, null, $variableValues);
}
} }