mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
Merge pull request #338 from simPod/cs-executor-test
Fix CS in tests/Executor
This commit is contained in:
commit
7f99bf478f
@ -1,79 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Error\Warning;
|
||||
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\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
|
||||
require_once __DIR__ . '/TestClasses.php';
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* DESCRIBE: Execute: Handles execution of abstract types with promises
|
||||
*/
|
||||
class AbstractPromiseTest extends TestCase
|
||||
{
|
||||
// DESCRIBE: Execute: Handles execution of abstract types with promises
|
||||
|
||||
/**
|
||||
* @see it('isTypeOf used to resolve runtime type for Interface')
|
||||
*/
|
||||
public function testIsTypeOfUsedToResolveRuntimeTypeForInterface() : void
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ]
|
||||
]
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [ $PetType ],
|
||||
'isTypeOf' => function($obj) {
|
||||
return new Deferred(function() use ($obj) {
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ],
|
||||
'woofs' => [ 'type' => Type::boolean() ],
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [ $PetType ],
|
||||
'isTypeOf' => function($obj) {
|
||||
return new Deferred(function() use ($obj) {
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ],
|
||||
'meows' => [ 'type' => Type::boolean() ],
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function() {
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false)
|
||||
new Cat('Garfield', false),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [ $CatType, $DogType ]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -93,10 +97,10 @@ class AbstractPromiseTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
[ 'name' => 'Odie', 'woofs' => true ],
|
||||
[ 'name' => 'Garfield', 'meows' => false ]
|
||||
]
|
||||
]
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
@ -107,58 +111,57 @@ class AbstractPromiseTest extends TestCase
|
||||
*/
|
||||
public function testIsTypeOfCanBeRejected() : void
|
||||
{
|
||||
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function () {
|
||||
'isTypeOf' => function () {
|
||||
return new Deferred(function () {
|
||||
throw new UserError('We are testing this error');
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false)
|
||||
new Cat('Garfield', false),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$CatType, $DogType]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -176,21 +179,21 @@ class AbstractPromiseTest extends TestCase
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [null, null]
|
||||
'data' => [
|
||||
'pets' => [null, null],
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'message' => 'We are testing this error',
|
||||
'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]],
|
||||
'path' => ['pets', 1]
|
||||
]
|
||||
]
|
||||
'path' => ['pets', 1],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset($expected, $result);
|
||||
@ -201,50 +204,49 @@ class AbstractPromiseTest extends TestCase
|
||||
*/
|
||||
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
||||
{
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'types' => [$DogType, $CatType]
|
||||
'name' => 'Pet',
|
||||
'types' => [$DogType, $CatType],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -266,9 +268,9 @@ class AbstractPromiseTest extends TestCase
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
]
|
||||
]
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
@ -280,7 +282,7 @@ class AbstractPromiseTest extends TestCase
|
||||
public function testResolveTypeOnInterfaceYieldsUsefulError() : void
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
@ -292,58 +294,59 @@ class AbstractPromiseTest extends TestCase
|
||||
if ($obj instanceof Human) {
|
||||
return $HumanType;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return new Deferred(function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false),
|
||||
new Human('Jon')
|
||||
new Human('Jon'),
|
||||
];
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$CatType, $DogType]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -361,20 +364,20 @@ class AbstractPromiseTest extends TestCase
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
null
|
||||
]
|
||||
null,
|
||||
],
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2]
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset($expected, $result);
|
||||
@ -385,32 +388,31 @@ class AbstractPromiseTest extends TestCase
|
||||
*/
|
||||
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
||||
{
|
||||
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
@ -422,28 +424,29 @@ class AbstractPromiseTest extends TestCase
|
||||
if ($obj instanceof Human) {
|
||||
return $HumanType;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'types' => [$DogType, $CatType]
|
||||
'types' => [$DogType, $CatType],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false),
|
||||
new Human('Jon')
|
||||
new Human('Jon'),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -462,20 +465,20 @@ class AbstractPromiseTest extends TestCase
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
null
|
||||
]
|
||||
null,
|
||||
],
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset($expected, $result);
|
||||
@ -487,7 +490,7 @@ class AbstractPromiseTest extends TestCase
|
||||
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
if ($obj instanceof Dog) {
|
||||
@ -496,49 +499,49 @@ class AbstractPromiseTest extends TestCase
|
||||
if ($obj instanceof Cat) {
|
||||
return 'Cat';
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false)
|
||||
new Cat('Garfield', false),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$CatType, $DogType]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -560,8 +563,8 @@ class AbstractPromiseTest extends TestCase
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
@ -571,53 +574,52 @@ class AbstractPromiseTest extends TestCase
|
||||
*/
|
||||
public function testResolveTypeCanBeCaught() : void
|
||||
{
|
||||
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function () {
|
||||
return new Deferred(function () {
|
||||
throw new UserError('We are testing this error');
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false)
|
||||
new Cat('Garfield', false),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$CatType, $DogType]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -635,21 +637,21 @@ class AbstractPromiseTest extends TestCase
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [null, null]
|
||||
'data' => [
|
||||
'pets' => [null, null],
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'message' => 'We are testing this error',
|
||||
'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]],
|
||||
'path' => ['pets', 1]
|
||||
]
|
||||
]
|
||||
'path' => ['pets', 1],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset($expected, $result);
|
||||
|
@ -1,23 +1,28 @@
|
||||
<?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\Executor;
|
||||
use GraphQL\GraphQL;
|
||||
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\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Execute: Handles execution of abstract types
|
||||
*/
|
||||
class AbstractTest extends TestCase
|
||||
{
|
||||
// Execute: Handles execution of abstract types
|
||||
|
||||
/**
|
||||
* @see it('isTypeOf used to resolve runtime type for Interface')
|
||||
*/
|
||||
@ -25,48 +30,50 @@ class AbstractTest extends TestCase
|
||||
{
|
||||
// isTypeOf used to resolve runtime type for Interface
|
||||
$petType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
// Added to interface type when defined
|
||||
$dogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$petType],
|
||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()]
|
||||
]
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$catType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$petType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($petType),
|
||||
'type' => Type::listOf($petType),
|
||||
'resolve' => function () {
|
||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$catType, $dogType]
|
||||
'types' => [$catType, $dogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -84,8 +91,8 @@ class AbstractTest extends TestCase
|
||||
$expected = new ExecutionResult([
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
]
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = Executor::execute($schema, Parser::parse($query));
|
||||
@ -98,42 +105,44 @@ class AbstractTest extends TestCase
|
||||
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
||||
{
|
||||
$dogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()]
|
||||
]
|
||||
'name' => 'Dog',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$catType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$petType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'types' => [$dogType, $catType]
|
||||
'name' => 'Pet',
|
||||
'types' => [$dogType, $catType],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($petType),
|
||||
'resolve' => function() {
|
||||
return [ new Dog('Odie', true), new Cat('Garfield', false) ];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
'type' => Type::listOf($petType),
|
||||
'resolve' => function () {
|
||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -151,8 +160,8 @@ class AbstractTest extends TestCase
|
||||
$expected = new ExecutionResult([
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
]
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals($expected, Executor::execute($schema, Parser::parse($query)));
|
||||
@ -161,14 +170,14 @@ class AbstractTest extends TestCase
|
||||
/**
|
||||
* @see it('resolveType on Interface yields useful error')
|
||||
*/
|
||||
function testResolveTypeOnInterfaceYieldsUsefulError()
|
||||
public function testResolveTypeOnInterfaceYieldsUsefulError() : void
|
||||
{
|
||||
$DogType = null;
|
||||
$CatType = null;
|
||||
$DogType = null;
|
||||
$CatType = null;
|
||||
$HumanType = null;
|
||||
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
return $DogType;
|
||||
@ -179,58 +188,58 @@ class AbstractTest extends TestCase
|
||||
if ($obj instanceof Human) {
|
||||
return $HumanType;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false),
|
||||
new Human('Jon')
|
||||
new Human('Jon'),
|
||||
];
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$DogType, $CatType]
|
||||
'types' => [$DogType, $CatType],
|
||||
]);
|
||||
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
@ -244,20 +253,21 @@ class AbstractTest extends TestCase
|
||||
}';
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
null
|
||||
]
|
||||
null,
|
||||
],
|
||||
],
|
||||
'errors' => [[
|
||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 11]],
|
||||
'path' => ['pets', 2]
|
||||
]]
|
||||
'locations' => [['line' => 2, 'column' => 11]],
|
||||
'path' => ['pets', 2],
|
||||
],
|
||||
],
|
||||
];
|
||||
$actual = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
$actual = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
|
||||
$this->assertArraySubset($expected, $actual);
|
||||
}
|
||||
@ -268,30 +278,30 @@ class AbstractTest extends TestCase
|
||||
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
||||
{
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
return $DogType;
|
||||
@ -303,25 +313,25 @@ class AbstractTest extends TestCase
|
||||
return $HumanType;
|
||||
}
|
||||
},
|
||||
'types' => [$DogType, $CatType]
|
||||
'types' => [$DogType, $CatType],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false),
|
||||
new Human('Jon')
|
||||
new Human('Jon'),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -337,22 +347,27 @@ class AbstractTest extends TestCase
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||
$expected = [
|
||||
'data' => [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie',
|
||||
'woofs' => true],
|
||||
['name' => 'Garfield',
|
||||
'meows' => false],
|
||||
null
|
||||
]
|
||||
[
|
||||
'name' => 'Odie',
|
||||
'woofs' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'Garfield',
|
||||
'meows' => false,
|
||||
],
|
||||
null,
|
||||
],
|
||||
],
|
||||
'errors' => [[
|
||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 11]],
|
||||
'path' => ['pets', 2]
|
||||
]]
|
||||
'locations' => [['line' => 2, 'column' => 11]],
|
||||
'path' => ['pets', 2],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertArraySubset($expected, $result);
|
||||
}
|
||||
@ -363,25 +378,25 @@ class AbstractTest extends TestCase
|
||||
public function testReturningInvalidValueFromResolveTypeYieldsUsefulError() : void
|
||||
{
|
||||
$fooInterface = new InterfaceType([
|
||||
'name' => 'FooInterface',
|
||||
'fields' => ['bar' => ['type' => Type::string()]],
|
||||
'name' => 'FooInterface',
|
||||
'fields' => ['bar' => ['type' => Type::string()]],
|
||||
'resolveType' => function () {
|
||||
return [];
|
||||
},
|
||||
]);
|
||||
|
||||
$fooObject = new ObjectType([
|
||||
'name' => 'FooObject',
|
||||
'fields' => ['bar' => ['type' => Type::string()]],
|
||||
'name' => 'FooObject',
|
||||
'fields' => ['bar' => ['type' => Type::string()]],
|
||||
'interfaces' => [$fooInterface],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'foo' => [
|
||||
'type' => $fooInterface,
|
||||
'type' => $fooInterface,
|
||||
'resolve' => function () {
|
||||
return 'dummy';
|
||||
},
|
||||
@ -394,18 +409,18 @@ class AbstractTest extends TestCase
|
||||
$result = GraphQL::executeQuery($schema, '{ foo { bar } }');
|
||||
|
||||
$expected = [
|
||||
'data' => ['foo' => null],
|
||||
'data' => ['foo' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'Internal server error',
|
||||
'message' => 'Internal server error',
|
||||
'debugMessage' =>
|
||||
'Abstract type FooInterface must resolve to an Object type at ' .
|
||||
'runtime for field Query.foo with value "dummy", received "[]". ' .
|
||||
'Either the FooInterface type should provide a "resolveType" ' .
|
||||
'function or each possible type should provide an "isTypeOf" function.',
|
||||
'locations' => [['line' => 1, 'column' => 3]],
|
||||
'path' => ['foo'],
|
||||
'category' => 'internal',
|
||||
'locations' => [['line' => 1, 'column' => 3]],
|
||||
'path' => ['foo'],
|
||||
'category' => 'internal',
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -418,51 +433,56 @@ class AbstractTest extends TestCase
|
||||
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function($obj) {
|
||||
if ($obj instanceof Dog) return 'Dog';
|
||||
if ($obj instanceof Cat) return 'Cat';
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) {
|
||||
if ($obj instanceof Dog) {
|
||||
return 'Dog';
|
||||
}
|
||||
if ($obj instanceof Cat) {
|
||||
return 'Cat';
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ]
|
||||
]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [ $PetType ],
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ],
|
||||
'woofs' => [ 'type' => Type::boolean() ],
|
||||
]
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [ $PetType ],
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ],
|
||||
'meows' => [ 'type' => Type::boolean() ],
|
||||
]
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function() {
|
||||
'type' => Type::listOf($PetType),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false)
|
||||
new Cat('Garfield', false),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [ $CatType, $DogType ]
|
||||
'types' => [$CatType, $DogType],
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -479,51 +499,52 @@ class AbstractTest extends TestCase
|
||||
|
||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||
|
||||
$this->assertEquals([
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
]
|
||||
]
|
||||
], $result);
|
||||
$this->assertEquals(
|
||||
[
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
],
|
||||
],
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
public function testHintsOnConflictingTypeInstancesInResolveType() : void
|
||||
{
|
||||
$createTest = function() use (&$iface) {
|
||||
$createTest = function () use (&$iface) {
|
||||
return new ObjectType([
|
||||
'name' => 'Test',
|
||||
'fields' => [
|
||||
'a' => Type::string()
|
||||
'name' => 'Test',
|
||||
'fields' => [
|
||||
'a' => Type::string(),
|
||||
],
|
||||
'interfaces' => function() use ($iface) {
|
||||
'interfaces' => function () use ($iface) {
|
||||
return [$iface];
|
||||
}
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
$iface = new InterfaceType([
|
||||
'name' => 'Node',
|
||||
'fields' => [
|
||||
'a' => Type::string()
|
||||
'name' => 'Node',
|
||||
'fields' => [
|
||||
'a' => Type::string(),
|
||||
],
|
||||
'resolveType' => function() use (&$createTest) {
|
||||
'resolveType' => function () use (&$createTest) {
|
||||
return $createTest();
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$query = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'node' => $iface,
|
||||
'test' => $createTest()
|
||||
]
|
||||
'test' => $createTest(),
|
||||
],
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $query,
|
||||
]);
|
||||
$schema = new Schema(['query' => $query]);
|
||||
$schema->assertValid();
|
||||
|
||||
$query = '
|
||||
@ -537,9 +558,9 @@ class AbstractTest extends TestCase
|
||||
$result = Executor::execute($schema, Parser::parse($query), ['node' => ['a' => 'value']]);
|
||||
|
||||
$this->assertEquals(
|
||||
'Schema must contain unique named types but contains multiple types named "Test". '.
|
||||
'Make sure that `resolveType` function of abstract type "Node" returns the same type instance '.
|
||||
'as referenced anywhere else within the schema '.
|
||||
'Schema must contain unique named types but contains multiple types named "Test". ' .
|
||||
'Make sure that `resolveType` function of abstract type "Node" returns the same type instance ' .
|
||||
'as referenced anywhere else within the schema ' .
|
||||
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
||||
$result->errors[0]->getMessage()
|
||||
);
|
||||
|
@ -1,33 +1,44 @@
|
||||
<?php
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Utils\Utils;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function in_array;
|
||||
|
||||
class DeferredFieldsTest extends TestCase
|
||||
{
|
||||
/** @var ObjectType */
|
||||
private $userType;
|
||||
|
||||
/** @var ObjectType */
|
||||
private $storyType;
|
||||
|
||||
/** @var ObjectType */
|
||||
private $categoryType;
|
||||
|
||||
/** @var */
|
||||
private $path;
|
||||
|
||||
/** @var mixed[][] */
|
||||
private $storyDataSource;
|
||||
|
||||
/** @var mixed[][] */
|
||||
private $userDataSource;
|
||||
|
||||
/** @var mixed[][] */
|
||||
private $categoryDataSource;
|
||||
|
||||
/** @var ObjectType */
|
||||
private $queryType;
|
||||
|
||||
public function setUp()
|
||||
@ -54,127 +65,152 @@ class DeferredFieldsTest extends TestCase
|
||||
$this->categoryDataSource = [
|
||||
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
|
||||
['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 = [];
|
||||
$this->userType = new ObjectType([
|
||||
'name' => 'User',
|
||||
'fields' => function() {
|
||||
'name' => 'User',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => Type::string(),
|
||||
'name' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return $user['name'];
|
||||
}
|
||||
},
|
||||
],
|
||||
'bestFriend' => [
|
||||
'type' => $this->userType,
|
||||
'resolve' => function($user, $args, $context, ResolveInfo $info) {
|
||||
'type' => $this->userType,
|
||||
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function() use ($user) {
|
||||
return new Deferred(function () use ($user) {
|
||||
$this->path[] = 'deferred-for-best-friend-of-' . $user['id'];
|
||||
return Utils::find($this->userDataSource, function($entry) use ($user) {
|
||||
return $entry['id'] === $user['bestFriendId'];
|
||||
});
|
||||
|
||||
return Utils::find(
|
||||
$this->userDataSource,
|
||||
function ($entry) use ($user) {
|
||||
return $entry['id'] === $user['bestFriendId'];
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$this->storyType = new ObjectType([
|
||||
'name' => 'Story',
|
||||
'name' => 'Story',
|
||||
'fields' => [
|
||||
'title' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function($entry, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
return $entry['title'];
|
||||
}
|
||||
],
|
||||
'author' => [
|
||||
'type' => $this->userType,
|
||||
'resolve' => function($story, $args, $context, ResolveInfo $info) {
|
||||
'title' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($entry, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function() use ($story) {
|
||||
return $entry['title'];
|
||||
},
|
||||
],
|
||||
'author' => [
|
||||
'type' => $this->userType,
|
||||
'resolve' => function ($story, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function () use ($story) {
|
||||
$this->path[] = 'deferred-for-story-' . $story['id'] . '-author';
|
||||
return Utils::find($this->userDataSource, function($entry) use ($story) {
|
||||
return $entry['id'] === $story['authorId'];
|
||||
});
|
||||
|
||||
return Utils::find(
|
||||
$this->userDataSource,
|
||||
function ($entry) use ($story) {
|
||||
return $entry['id'] === $story['authorId'];
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->categoryType = new ObjectType([
|
||||
'name' => 'Category',
|
||||
'name' => 'Category',
|
||||
'fields' => [
|
||||
'name' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return $category['name'];
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
'stories' => [
|
||||
'type' => Type::listOf($this->storyType),
|
||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
||||
'stories' => [
|
||||
'type' => Type::listOf($this->storyType),
|
||||
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
return Utils::filter($this->storyDataSource, function($story) use ($category) {
|
||||
return in_array($category['id'], $story['categoryIds']);
|
||||
});
|
||||
}
|
||||
|
||||
return Utils::filter(
|
||||
$this->storyDataSource,
|
||||
function ($story) use ($category) {
|
||||
return in_array($category['id'], $story['categoryIds']);
|
||||
}
|
||||
);
|
||||
},
|
||||
],
|
||||
'topStory' => [
|
||||
'type' => $this->storyType,
|
||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
||||
'type' => $this->storyType,
|
||||
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function () use ($category) {
|
||||
$this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory';
|
||||
return Utils::find($this->storyDataSource, function($story) use ($category) {
|
||||
return $story['id'] === $category['topStoryId'];
|
||||
});
|
||||
|
||||
return Utils::find(
|
||||
$this->storyDataSource,
|
||||
function ($story) use ($category) {
|
||||
return $story['id'] === $category['topStoryId'];
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->queryType = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'topStories' => [
|
||||
'type' => Type::listOf($this->storyType),
|
||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
||||
'topStories' => [
|
||||
'type' => Type::listOf($this->storyType),
|
||||
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
return Utils::filter($this->storyDataSource, function($story) {
|
||||
return $story['id'] % 2 === 1;
|
||||
});
|
||||
}
|
||||
|
||||
return Utils::filter(
|
||||
$this->storyDataSource,
|
||||
function ($story) {
|
||||
return $story['id'] % 2 === 1;
|
||||
}
|
||||
);
|
||||
},
|
||||
],
|
||||
'featuredCategory' => [
|
||||
'type' => $this->categoryType,
|
||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
||||
'type' => $this->categoryType,
|
||||
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return $this->categoryDataSource[0];
|
||||
}
|
||||
},
|
||||
],
|
||||
'categories' => [
|
||||
'type' => Type::listOf($this->categoryType),
|
||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
||||
'categories' => [
|
||||
'type' => Type::listOf($this->categoryType),
|
||||
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return $this->categoryDataSource;
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
parent::setUp();
|
||||
@ -202,12 +238,12 @@ class DeferredFieldsTest extends TestCase
|
||||
');
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $this->queryType
|
||||
'query' => $this->queryType,
|
||||
]);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'topStories' => [
|
||||
'topStories' => [
|
||||
['title' => 'Story #1', 'author' => ['name' => 'John']],
|
||||
['title' => 'Story #3', 'author' => ['name' => 'Joe']],
|
||||
['title' => 'Story #5', 'author' => ['name' => 'John']],
|
||||
@ -220,9 +256,9 @@ class DeferredFieldsTest extends TestCase
|
||||
['title' => 'Story #4', 'author' => ['name' => 'Joe']],
|
||||
['title' => 'Story #6', 'author' => ['name' => 'Jane']],
|
||||
['title' => 'Story #8', 'author' => ['name' => 'John']],
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$result = Executor::execute($schema, $query);
|
||||
@ -292,12 +328,12 @@ class DeferredFieldsTest extends TestCase
|
||||
');
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $this->queryType
|
||||
'query' => $this->queryType,
|
||||
]);
|
||||
|
||||
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
|
||||
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']];
|
||||
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
|
||||
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
|
||||
$author4 = ['name' => 'Dirk', 'bestFriend' => ['name' => 'John']];
|
||||
|
||||
$expected = [
|
||||
@ -306,8 +342,8 @@ class DeferredFieldsTest extends TestCase
|
||||
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
|
||||
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
|
||||
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$result = Executor::execute($schema, $query);
|
||||
@ -352,54 +388,56 @@ class DeferredFieldsTest extends TestCase
|
||||
public function testComplexRecursiveDeferredFields() : void
|
||||
{
|
||||
$complexType = new ObjectType([
|
||||
'name' => 'ComplexType',
|
||||
'fields' => function() use (&$complexType) {
|
||||
'name' => 'ComplexType',
|
||||
'fields' => function () use (&$complexType) {
|
||||
return [
|
||||
'sync' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
return 'sync';
|
||||
}
|
||||
],
|
||||
'deferred' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
||||
'sync' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function() use ($info) {
|
||||
return 'sync';
|
||||
},
|
||||
],
|
||||
'deferred' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function () use ($info) {
|
||||
$this->path[] = ['!dfd for: ', $info->path];
|
||||
|
||||
return 'deferred';
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
'nest' => [
|
||||
'type' => $complexType,
|
||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
||||
'nest' => [
|
||||
'type' => $complexType,
|
||||
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return [];
|
||||
}
|
||||
},
|
||||
],
|
||||
'deferredNest' => [
|
||||
'type' => $complexType,
|
||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
||||
'type' => $complexType,
|
||||
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||
$this->path[] = $info->path;
|
||||
|
||||
return new Deferred(function() use ($info) {
|
||||
return new Deferred(function () use ($info) {
|
||||
$this->path[] = ['!dfd nest for: ', $info->path];
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $complexType
|
||||
]);
|
||||
$schema = new Schema(['query' => $complexType]);
|
||||
|
||||
$query = Parser::parse('
|
||||
$query = Parser::parse('
|
||||
{
|
||||
nest {
|
||||
sync
|
||||
@ -427,34 +465,34 @@ class DeferredFieldsTest extends TestCase
|
||||
}
|
||||
}
|
||||
');
|
||||
$result = Executor::execute($schema, $query);
|
||||
$result = Executor::execute($schema, $query);
|
||||
$expected = [
|
||||
'data' => [
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred'
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
],
|
||||
'deferredNest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred'
|
||||
]
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
],
|
||||
],
|
||||
'deferredNest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred'
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
'nest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
],
|
||||
'deferredNest' => [
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred'
|
||||
]
|
||||
]
|
||||
]
|
||||
'sync' => 'sync',
|
||||
'deferred' => 'deferred',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
@ -1,16 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Language\Source;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Describe: Execute: handles directives
|
||||
*/
|
||||
class DirectivesTest extends TestCase
|
||||
{
|
||||
// Describe: Execute: handles directives
|
||||
/** @var Schema */
|
||||
private static $schema;
|
||||
|
||||
/** @var string[] */
|
||||
private static $data;
|
||||
|
||||
/**
|
||||
* @see it('basic query works')
|
||||
@ -20,10 +31,50 @@ class DirectivesTest extends TestCase
|
||||
$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
|
||||
{
|
||||
// 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
|
||||
$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) }')
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Executor\ExecutionResult;
|
||||
|
@ -1,99 +1,119 @@
|
||||
<?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\Warning;
|
||||
use GraphQL\Executor\ExecutionResult;
|
||||
use GraphQL\Executor\Executor;
|
||||
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\EnumType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\ScalarType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\Error\Error;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function count;
|
||||
|
||||
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 = [];
|
||||
|
||||
/** @var bool[] */
|
||||
public $loadedTypes = [];
|
||||
|
||||
public function testWarnsAboutSlowIsTypeOfForLazySchema() : void
|
||||
{
|
||||
// isTypeOf used to resolve runtime type for Interface
|
||||
$petType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'fields' => function() {
|
||||
'name' => 'Pet',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => ['type' => Type::string()]
|
||||
'name' => ['type' => Type::string()],
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
// Added to interface type when defined
|
||||
$dogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$petType],
|
||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
||||
'fields' => function() {
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
},
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()]
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$catType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$petType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
'isTypeOf' => function ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
},
|
||||
'fields' => function() {
|
||||
'fields' => function () {
|
||||
return [
|
||||
'name' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'pets' => [
|
||||
'type' => Type::listOf($petType),
|
||||
'type' => Type::listOf($petType),
|
||||
'resolve' => function () {
|
||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'types' => [$catType, $dogType],
|
||||
'typeLoader' => function($name) use ($dogType, $petType, $catType) {
|
||||
'types' => [$catType, $dogType],
|
||||
'typeLoader' => function ($name) use ($dogType, $petType, $catType) {
|
||||
switch ($name) {
|
||||
case 'Dog':
|
||||
return $dogType;
|
||||
@ -102,7 +122,7 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
case 'Cat':
|
||||
return $catType;
|
||||
}
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
@ -120,7 +140,7 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
$expected = new ExecutionResult([
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
],
|
||||
]);
|
||||
|
||||
@ -134,43 +154,46 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
$this->assertInstanceOf(Error::class, $result->errors[0]->getPrevious());
|
||||
|
||||
$this->assertEquals(
|
||||
'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 '.
|
||||
'implementations. It requires full schema scan and degrades query performance significantly. '.
|
||||
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of ' .
|
||||
'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. ' .
|
||||
'Make sure your `resolveType` always returns valid implementation or throws.',
|
||||
$result->errors[0]->getMessage());
|
||||
$result->errors[0]->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testHintsOnConflictingTypeInstancesInDefinitions() : void
|
||||
{
|
||||
$calls = [];
|
||||
$typeLoader = function($name) use (&$calls) {
|
||||
$calls = [];
|
||||
$typeLoader = function ($name) use (&$calls) {
|
||||
$calls[] = $name;
|
||||
switch ($name) {
|
||||
case 'Test':
|
||||
return new ObjectType([
|
||||
'name' => 'Test',
|
||||
'fields' => function() {
|
||||
'name' => 'Test',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'test' => Type::string(),
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
$query = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => function() use ($typeLoader) {
|
||||
'name' => 'Query',
|
||||
'fields' => function () use ($typeLoader) {
|
||||
return [
|
||||
'test' => $typeLoader('Test')
|
||||
'test' => $typeLoader('Test'),
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $query,
|
||||
'typeLoader' => $typeLoader
|
||||
'query' => $query,
|
||||
'typeLoader' => $typeLoader,
|
||||
]);
|
||||
|
||||
$query = '
|
||||
@ -186,8 +209,8 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
$this->assertEquals(['Test', 'Test'], $calls);
|
||||
|
||||
$this->assertEquals(
|
||||
'Schema must contain unique named types but contains multiple types named "Test". '.
|
||||
'Make sure that type loader returns the same instance as defined in Query.test '.
|
||||
'Schema must contain unique named types but contains multiple types named "Test". ' .
|
||||
'Make sure that type loader returns the same instance as defined in Query.test ' .
|
||||
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
||||
$result->errors[0]->getMessage()
|
||||
);
|
||||
@ -200,54 +223,161 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
public function testSimpleQuery() : void
|
||||
{
|
||||
$schema = new Schema([
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function($name) {
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function ($name) {
|
||||
return $this->loadType($name, true);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$query = '{ object { string } }';
|
||||
$query = '{ object { string } }';
|
||||
$result = Executor::execute(
|
||||
$schema,
|
||||
Parser::parse($query),
|
||||
['object' => ['string' => 'test']]
|
||||
);
|
||||
|
||||
$expected = [
|
||||
$expected = [
|
||||
'data' => ['object' => ['string' => 'test']],
|
||||
];
|
||||
$expectedExecutorCalls = [
|
||||
'Query.fields',
|
||||
'SomeObject',
|
||||
'SomeObject.fields'
|
||||
'SomeObject.fields',
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray(true));
|
||||
$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
|
||||
{
|
||||
$schema = new Schema([
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function($name) {
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function ($name) {
|
||||
return $this->loadType($name, true);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$query = '{ object { object { object { string } } } }';
|
||||
$query = '{ object { object { object { string } } } }';
|
||||
$result = Executor::execute(
|
||||
$schema,
|
||||
Parser::parse($query),
|
||||
['object' => ['object' => ['object' => ['string' => 'test']]]]
|
||||
);
|
||||
|
||||
$expected = [
|
||||
'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]]
|
||||
$expected = [
|
||||
'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]],
|
||||
];
|
||||
$expectedLoadedTypes = [
|
||||
'Query' => true,
|
||||
'SomeObject' => true,
|
||||
'OtherObject' => true
|
||||
'Query' => true,
|
||||
'SomeObject' => true,
|
||||
'OtherObject' => true,
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray(true));
|
||||
@ -256,7 +386,7 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
$expectedExecutorCalls = [
|
||||
'Query.fields',
|
||||
'SomeObject',
|
||||
'SomeObject.fields'
|
||||
'SomeObject.fields',
|
||||
];
|
||||
$this->assertEquals($expectedExecutorCalls, $this->calls);
|
||||
}
|
||||
@ -264,13 +394,13 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
public function testResolveUnion() : void
|
||||
{
|
||||
$schema = new Schema([
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function($name) {
|
||||
'query' => $this->loadType('Query'),
|
||||
'typeLoader' => function ($name) {
|
||||
return $this->loadType($name, true);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$query = '
|
||||
$query = '
|
||||
{
|
||||
other {
|
||||
union {
|
||||
@ -285,17 +415,17 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
['other' => ['union' => ['scalar' => 'test']]]
|
||||
);
|
||||
|
||||
$expected = [
|
||||
$expected = [
|
||||
'data' => ['other' => ['union' => ['scalar' => 'test']]],
|
||||
];
|
||||
$expectedLoadedTypes = [
|
||||
'Query' => true,
|
||||
'SomeObject' => true,
|
||||
'OtherObject' => true,
|
||||
'SomeUnion' => true,
|
||||
'Query' => true,
|
||||
'SomeObject' => true,
|
||||
'OtherObject' => true,
|
||||
'SomeUnion' => true,
|
||||
'SomeInterface' => true,
|
||||
'DeeperObject' => true,
|
||||
'SomeScalar' => true,
|
||||
'DeeperObject' => true,
|
||||
'SomeScalar' => true,
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray(true));
|
||||
@ -313,98 +443,4 @@ class ExecutorLazySchemaTest extends TestCase
|
||||
];
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function sprintf;
|
||||
|
||||
class ExecutorSchemaTest extends TestCase
|
||||
{
|
||||
// Execute: Handles execution with a complex schema
|
||||
|
||||
/**
|
||||
* @see it('executes using a schema')
|
||||
*/
|
||||
public function testExecutesUsingASchema() : void
|
||||
{
|
||||
$BlogArticle = null;
|
||||
$BlogImage = new ObjectType([
|
||||
'name' => 'Image',
|
||||
$BlogImage = new ObjectType([
|
||||
'name' => 'Image',
|
||||
'fields' => [
|
||||
'url' => ['type' => Type::string()],
|
||||
'width' => ['type' => Type::int()],
|
||||
'url' => ['type' => Type::string()],
|
||||
'width' => ['type' => Type::int()],
|
||||
'height' => ['type' => Type::int()],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
$BlogAuthor = new ObjectType([
|
||||
'name' => 'Author',
|
||||
'fields' => function() use (&$BlogArticle, &$BlogImage) {
|
||||
'name' => 'Author',
|
||||
'fields' => function () use (&$BlogArticle, &$BlogImage) {
|
||||
return [
|
||||
'id' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'pic' => [
|
||||
'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]],
|
||||
'type' => $BlogImage,
|
||||
'id' => ['type' => Type::string()],
|
||||
'name' => ['type' => Type::string()],
|
||||
'pic' => [
|
||||
'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]],
|
||||
'type' => $BlogImage,
|
||||
'resolve' => function ($obj, $args) {
|
||||
return $obj['pic']($args['width'], $args['height']);
|
||||
}
|
||||
},
|
||||
],
|
||||
'recentArticle' => $BlogArticle
|
||||
'recentArticle' => $BlogArticle,
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$BlogArticle = new ObjectType([
|
||||
'name' => 'Article',
|
||||
'name' => 'Article',
|
||||
'fields' => [
|
||||
'id' => ['type' => Type::nonNull(Type::string())],
|
||||
'id' => ['type' => Type::nonNull(Type::string())],
|
||||
'isPublished' => ['type' => Type::boolean()],
|
||||
'author' => ['type' => $BlogAuthor],
|
||||
'title' => ['type' => Type::string()],
|
||||
'body' => ['type' => Type::string()],
|
||||
'keywords' => ['type' => Type::listOf(Type::string())]
|
||||
]
|
||||
'author' => ['type' => $BlogAuthor],
|
||||
'title' => ['type' => Type::string()],
|
||||
'body' => ['type' => Type::string()],
|
||||
'keywords' => ['type' => Type::listOf(Type::string())],
|
||||
],
|
||||
]);
|
||||
|
||||
$BlogQuery = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'article' => [
|
||||
'type' => $BlogArticle,
|
||||
'args' => ['id' => ['type' => Type::id()]],
|
||||
'type' => $BlogArticle,
|
||||
'args' => ['id' => ['type' => Type::id()]],
|
||||
'resolve' => function ($_, $args) {
|
||||
return $this->article($args['id']);
|
||||
}
|
||||
},
|
||||
],
|
||||
'feed' => [
|
||||
'type' => Type::listOf($BlogArticle),
|
||||
'feed' => [
|
||||
'type' => Type::listOf($BlogArticle),
|
||||
'resolve' => function () {
|
||||
return [
|
||||
$this->article(1),
|
||||
@ -80,16 +83,15 @@ class ExecutorSchemaTest extends TestCase
|
||||
$this->article(7),
|
||||
$this->article(8),
|
||||
$this->article(9),
|
||||
$this->article(10)
|
||||
$this->article(10),
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$BlogSchema = new Schema(['query' => $BlogQuery]);
|
||||
|
||||
|
||||
$request = '
|
||||
{
|
||||
feed {
|
||||
@ -126,51 +128,71 @@ class ExecutorSchemaTest extends TestCase
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'feed' => [
|
||||
['id' => '1',
|
||||
'title' => 'My Article 1'],
|
||||
['id' => '2',
|
||||
'title' => 'My Article 2'],
|
||||
['id' => '3',
|
||||
'title' => 'My Article 3'],
|
||||
['id' => '4',
|
||||
'title' => 'My Article 4'],
|
||||
['id' => '5',
|
||||
'title' => 'My Article 5'],
|
||||
['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']
|
||||
'feed' => [
|
||||
[
|
||||
'id' => '1',
|
||||
'title' => 'My Article 1',
|
||||
],
|
||||
[
|
||||
'id' => '2',
|
||||
'title' => 'My Article 2',
|
||||
],
|
||||
[
|
||||
'id' => '3',
|
||||
'title' => 'My Article 3',
|
||||
],
|
||||
[
|
||||
'id' => '4',
|
||||
'title' => 'My Article 4',
|
||||
],
|
||||
[
|
||||
'id' => '5',
|
||||
'title' => 'My Article 5',
|
||||
],
|
||||
[
|
||||
'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' => [
|
||||
'id' => '1',
|
||||
'id' => '1',
|
||||
'isPublished' => true,
|
||||
'title' => 'My Article 1',
|
||||
'body' => 'This is a post',
|
||||
'author' => [
|
||||
'id' => '123',
|
||||
'name' => 'John Smith',
|
||||
'pic' => [
|
||||
'url' => 'cdn://123',
|
||||
'width' => 640,
|
||||
'height' => 480
|
||||
'title' => 'My Article 1',
|
||||
'body' => 'This is a post',
|
||||
'author' => [
|
||||
'id' => '123',
|
||||
'name' => 'John Smith',
|
||||
'pic' => [
|
||||
'url' => 'cdn://123',
|
||||
'width' => 640,
|
||||
'height' => 480,
|
||||
],
|
||||
'recentArticle' => [
|
||||
'id' => '1',
|
||||
'id' => '1',
|
||||
'isPublished' => true,
|
||||
'title' => 'My Article 1',
|
||||
'body' => 'This is a post',
|
||||
'keywords' => ['foo', 'bar', '1', 'true', null]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
'title' => 'My Article 1',
|
||||
'body' => 'This is a post',
|
||||
'keywords' => ['foo', 'bar', '1', 'true', null],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray());
|
||||
@ -179,30 +201,32 @@ class ExecutorSchemaTest extends TestCase
|
||||
private function article($id)
|
||||
{
|
||||
$johnSmith = null;
|
||||
$article = function($id) use (&$johnSmith) {
|
||||
$article = function ($id) use (&$johnSmith) {
|
||||
return [
|
||||
'id' => $id,
|
||||
'id' => $id,
|
||||
'isPublished' => 'true',
|
||||
'author' => $johnSmith,
|
||||
'title' => 'My Article ' . $id,
|
||||
'body' => 'This is a post',
|
||||
'hidden' => 'This data is not exposed in the schema',
|
||||
'keywords' => ['foo', 'bar', 1, true, null]
|
||||
'author' => $johnSmith,
|
||||
'title' => 'My Article ' . $id,
|
||||
'body' => 'This is a post',
|
||||
'hidden' => 'This data is not exposed in the schema',
|
||||
'keywords' => ['foo', 'bar', 1, true, null],
|
||||
];
|
||||
};
|
||||
|
||||
$getPic = function($uid, $width, $height) {
|
||||
$getPic = function ($uid, $width, $height) {
|
||||
return [
|
||||
'url' => "cdn://$uid",
|
||||
'width' => $width,
|
||||
'height' => $height
|
||||
'url' => sprintf('cdn://%s', $uid),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
];
|
||||
};
|
||||
|
||||
$johnSmith = [
|
||||
'id' => 123,
|
||||
'name' => 'John Smith',
|
||||
'pic' => function($width, $height) use ($getPic) {return $getPic(123, $width, $height);},
|
||||
'id' => 123,
|
||||
'name' => 'John Smith',
|
||||
'pic' => function ($width, $height) use ($getPic) {
|
||||
return $getPic(123, $width, $height);
|
||||
},
|
||||
'recentArticle' => $article(1),
|
||||
];
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,104 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @author: Ivo Meißner
|
||||
* Date: 03.05.16
|
||||
* Time: 13:14
|
||||
*/
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LazyInterfaceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Schema
|
||||
*/
|
||||
/** @var Schema */
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* @var InterfaceType
|
||||
*/
|
||||
/** @var InterfaceType */
|
||||
protected $lazyInterface;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
/** @var ObjectType */
|
||||
protected $testObject;
|
||||
|
||||
/**
|
||||
* Setup schema
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$query = new ObjectType([
|
||||
'name' => 'query',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'lazyInterface' => [
|
||||
'type' => $this->getLazyInterfaceType(),
|
||||
'resolve' => function() {
|
||||
return [];
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LazyInterface
|
||||
*
|
||||
* @return InterfaceType
|
||||
*/
|
||||
protected function getLazyInterfaceType()
|
||||
{
|
||||
if (!$this->lazyInterface) {
|
||||
$this->lazyInterface = new InterfaceType([
|
||||
'name' => 'LazyInterface',
|
||||
'fields' => [
|
||||
'a' => Type::string()
|
||||
],
|
||||
'resolveType' => function() {
|
||||
return $this->getTestObjectType();
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->lazyInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test ObjectType
|
||||
* @return ObjectType
|
||||
*/
|
||||
protected function getTestObjectType()
|
||||
{
|
||||
if (!$this->testObject) {
|
||||
$this->testObject = new ObjectType([
|
||||
'name' => 'TestObject',
|
||||
'fields' => [
|
||||
'name' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function() {
|
||||
return 'testname';
|
||||
}
|
||||
]
|
||||
],
|
||||
'interfaces' => [$this->getLazyInterfaceType()]
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->testObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles execution of a lazily created interface
|
||||
*/
|
||||
@ -116,12 +46,78 @@ class LazyInterfaceTest extends TestCase
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'lazyInterface' => [
|
||||
'name' => 'testname'
|
||||
]
|
||||
]
|
||||
'lazyInterface' => ['name' => 'testname'],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup schema
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$query = new ObjectType([
|
||||
'name' => 'query',
|
||||
'fields' => function () {
|
||||
return [
|
||||
'lazyInterface' => [
|
||||
'type' => $this->getLazyInterfaceType(),
|
||||
'resolve' => function () {
|
||||
return [];
|
||||
},
|
||||
],
|
||||
];
|
||||
},
|
||||
]);
|
||||
|
||||
$this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LazyInterface
|
||||
*
|
||||
* @return InterfaceType
|
||||
*/
|
||||
protected function getLazyInterfaceType()
|
||||
{
|
||||
if (! $this->lazyInterface) {
|
||||
$this->lazyInterface = new InterfaceType([
|
||||
'name' => 'LazyInterface',
|
||||
'fields' => [
|
||||
'a' => Type::string(),
|
||||
],
|
||||
'resolveType' => function () {
|
||||
return $this->getTestObjectType();
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->lazyInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test ObjectType
|
||||
* @return ObjectType
|
||||
*/
|
||||
protected function getTestObjectType()
|
||||
{
|
||||
if (! $this->testObject) {
|
||||
$this->testObject = new ObjectType([
|
||||
'name' => 'TestObject',
|
||||
'fields' => [
|
||||
'name' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function () {
|
||||
return 'testname';
|
||||
},
|
||||
],
|
||||
],
|
||||
'interfaces' => [$this->getLazyInterfaceType()],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->testObject;
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Language\SourceLocation;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ListsTest extends TestCase
|
||||
{
|
||||
// Describe: Execute: Handles list nullability
|
||||
|
||||
/**
|
||||
* [T]
|
||||
*/
|
||||
@ -24,23 +23,57 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNullableLists(
|
||||
[ 1, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
[1, 2],
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNullableLists(
|
||||
[ 1, null, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
[1, null, 2],
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNullableLists(
|
||||
null,
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
}
|
||||
|
||||
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]
|
||||
*/
|
||||
@ -48,26 +81,26 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNullableLists(
|
||||
new Deferred(function() {
|
||||
return [1,2];
|
||||
new Deferred(function () {
|
||||
return [1, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNullableLists(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, null, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNullableLists(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
|
||||
// Rejected
|
||||
@ -78,14 +111,14 @@ class ListsTest extends TestCase
|
||||
});
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test']
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test'],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -98,57 +131,64 @@ class ListsTest extends TestCase
|
||||
// Contains values
|
||||
$this->checkHandlesNullableLists(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
}),
|
||||
],
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNullableLists(
|
||||
[
|
||||
new Deferred(function() {return 1;}),
|
||||
new Deferred(function() {return null;}),
|
||||
new Deferred(function() {return 2;})
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
}),
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNullableLists(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
|
||||
// Contains reject
|
||||
$this->checkHandlesNullableLists(
|
||||
function () {
|
||||
return [
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
];
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test', 1]
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test', 1],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -160,32 +200,38 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullableLists(
|
||||
[ 1, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
[1, 2],
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullableLists(
|
||||
[ 1, null, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
[1, null, 2],
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNonNullableLists(
|
||||
null,
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [['line' => 1, 'column' => 10]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
|
||||
{
|
||||
$testType = Type::nonNull(Type::listOf(Type::int()));
|
||||
$this->check($testType, $testData, $expected, $debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* [T]!
|
||||
*/
|
||||
@ -193,31 +239,31 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullableLists(
|
||||
new Deferred(function() {
|
||||
return [1,2];
|
||||
new Deferred(function () {
|
||||
return [1, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullableLists(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, null, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNonNullableLists(
|
||||
null,
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [['line' => 1, 'column' => 10]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
@ -225,19 +271,19 @@ class ListsTest extends TestCase
|
||||
// Rejected
|
||||
$this->checkHandlesNonNullableLists(
|
||||
function () {
|
||||
return new Deferred(function() {
|
||||
return new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
});
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => null],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test']
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test'],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -250,56 +296,56 @@ class ListsTest extends TestCase
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullableLists(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullableLists(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||
);
|
||||
|
||||
// Contains reject
|
||||
$this->checkHandlesNonNullableLists(
|
||||
function () {
|
||||
return [
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
];
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test', 1]
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test', 1],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -311,21 +357,21 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
[ 1, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
[1, 2],
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
[ 1, null, 2 ],
|
||||
[1, null, 2],
|
||||
[
|
||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
@ -333,10 +379,16 @@ class ListsTest extends TestCase
|
||||
// Returns null
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
null,
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
}
|
||||
|
||||
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
|
||||
{
|
||||
$testType = Type::listOf(Type::nonNull(Type::int()));
|
||||
$this->check($testType, $testData, $expected, $debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* [T!]
|
||||
*/
|
||||
@ -344,53 +396,53 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, null, 2];
|
||||
}),
|
||||
[
|
||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [['line' => 1, 'column' => 10]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
|
||||
// Rejected
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
function () {
|
||||
return new Deferred(function() {
|
||||
return new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
});
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test']
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test'],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -403,50 +455,56 @@ class ListsTest extends TestCase
|
||||
// Contains values
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
[
|
||||
new Deferred(function() {return 1;}),
|
||||
new Deferred(function() {return null;}),
|
||||
new Deferred(function() {return 2;})
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
}),
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
||||
['data' => ['nest' => ['test' => null]]]
|
||||
);
|
||||
|
||||
// Contains reject
|
||||
$this->checkHandlesListOfNonNulls(
|
||||
function () {
|
||||
return [
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
];
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'data' => ['nest' => ['test' => null]],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test', 1]
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test', 1],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -458,22 +516,21 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
[ 1, 2 ],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
[1, 2],
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
[ 1, null, 2 ],
|
||||
[1, null, 2],
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [['line' => 1, 'column' => 10 ]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
@ -482,18 +539,24 @@ class ListsTest extends TestCase
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
null,
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
|
||||
{
|
||||
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
|
||||
$this->check($testType, $testData, $expected, $debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* [T!]!
|
||||
*/
|
||||
@ -501,42 +564,42 @@ class ListsTest extends TestCase
|
||||
{
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, 2];
|
||||
}),
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return [1, null, 2];
|
||||
}),
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
// Returns null
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
@ -544,19 +607,19 @@ class ListsTest extends TestCase
|
||||
// Rejected
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
function () {
|
||||
return new Deferred(function() {
|
||||
return new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
});
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
'path' => ['nest', 'test']
|
||||
]
|
||||
]
|
||||
'path' => ['nest', 'test'],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -569,38 +632,38 @@ class ListsTest extends TestCase
|
||||
// Contains values
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
|
||||
],
|
||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
||||
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||
);
|
||||
|
||||
// Contains null
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
[
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return null;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
],
|
||||
[
|
||||
'data' => [ 'nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||
'locations' => [['line' => 1, 'column' => 10]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 1, 'column' => 10]],
|
||||
],
|
||||
],
|
||||
],
|
||||
true
|
||||
);
|
||||
@ -609,83 +672,27 @@ class ListsTest extends TestCase
|
||||
$this->checkHandlesNonNullListOfNonNulls(
|
||||
function () {
|
||||
return [
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 1;
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
throw new UserError('bad');
|
||||
}),
|
||||
new Deferred(function() {
|
||||
new Deferred(function () {
|
||||
return 2;
|
||||
})
|
||||
}),
|
||||
];
|
||||
},
|
||||
[
|
||||
'data' => ['nest' => null ],
|
||||
'data' => ['nest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'bad',
|
||||
'message' => 'bad',
|
||||
'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));
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Executor\ExecutionResult;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Language\SourceLocation;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Tests\Executor\TestClasses\Root;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MutationsTest extends TestCase
|
||||
{
|
||||
// Execute: Handles mutation execution ordering
|
||||
|
||||
/**
|
||||
* @see it('evaluates mutations serially')
|
||||
*/
|
||||
public function testEvaluatesMutationsSerially() : void
|
||||
{
|
||||
$doc = 'mutation M {
|
||||
$doc = 'mutation M {
|
||||
first: immediatelyChangeTheNumber(newNumber: 1) {
|
||||
theNumber
|
||||
},
|
||||
@ -38,36 +37,79 @@ class MutationsTest extends TestCase
|
||||
theNumber
|
||||
}
|
||||
}';
|
||||
$ast = Parser::parse($doc);
|
||||
$ast = Parser::parse($doc);
|
||||
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
||||
$expected = [
|
||||
$expected = [
|
||||
'data' => [
|
||||
'first' => [
|
||||
'theNumber' => 1
|
||||
],
|
||||
'second' => [
|
||||
'theNumber' => 2
|
||||
],
|
||||
'third' => [
|
||||
'theNumber' => 3
|
||||
],
|
||||
'fourth' => [
|
||||
'theNumber' => 4
|
||||
],
|
||||
'fifth' => [
|
||||
'theNumber' => 5
|
||||
]
|
||||
]
|
||||
'first' => ['theNumber' => 1],
|
||||
'second' => ['theNumber' => 2],
|
||||
'third' => ['theNumber' => 3],
|
||||
'fourth' => ['theNumber' => 4],
|
||||
'fifth' => ['theNumber' => 5],
|
||||
],
|
||||
];
|
||||
$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')
|
||||
*/
|
||||
public function testEvaluatesMutationsCorrectlyInThePresenseOfAFailedMutation() : void
|
||||
{
|
||||
$doc = 'mutation M {
|
||||
$doc = 'mutation M {
|
||||
first: immediatelyChangeTheNumber(newNumber: 1) {
|
||||
theNumber
|
||||
},
|
||||
@ -87,147 +129,28 @@ class MutationsTest extends TestCase
|
||||
theNumber
|
||||
}
|
||||
}';
|
||||
$ast = Parser::parse($doc);
|
||||
$ast = Parser::parse($doc);
|
||||
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
||||
$expected = [
|
||||
'data' => [
|
||||
'first' => [
|
||||
'theNumber' => 1
|
||||
],
|
||||
'second' => [
|
||||
'theNumber' => 2
|
||||
],
|
||||
'third' => null,
|
||||
'fourth' => [
|
||||
'theNumber' => 4
|
||||
],
|
||||
'fifth' => [
|
||||
'theNumber' => 5
|
||||
],
|
||||
'sixth' => null,
|
||||
$expected = [
|
||||
'data' => [
|
||||
'first' => ['theNumber' => 1],
|
||||
'second' => ['theNumber' => 2],
|
||||
'third' => null,
|
||||
'fourth' => ['theNumber' => 4],
|
||||
'fifth' => ['theNumber' => 5],
|
||||
'sixth' => null,
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot change the number',
|
||||
'locations' => [['line' => 8, 'column' => 7]]
|
||||
'locations' => [['line' => 8, 'column' => 7]],
|
||||
],
|
||||
[
|
||||
'debugMessage' => 'Cannot change the number',
|
||||
'locations' => [['line' => 17, 'column' => 7]]
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 17, 'column' => 7]],
|
||||
],
|
||||
],
|
||||
];
|
||||
$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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Error\UserError;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Language\SourceLocation;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NonNullTest extends TestCase
|
||||
@ -27,41 +29,46 @@ class NonNullTest extends TestCase
|
||||
/** @var \Exception */
|
||||
public $promiseNonNullError;
|
||||
|
||||
/** @var callable[] */
|
||||
public $throwingData;
|
||||
|
||||
/** @var callable[] */
|
||||
public $nullingData;
|
||||
|
||||
/** @var Schema */
|
||||
public $schema;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->syncError = new UserError('sync');
|
||||
$this->syncNonNullError = new UserError('syncNonNull');
|
||||
$this->promiseError = new UserError('promise');
|
||||
$this->syncError = new UserError('sync');
|
||||
$this->syncNonNullError = new UserError('syncNonNull');
|
||||
$this->promiseError = new UserError('promise');
|
||||
$this->promiseNonNullError = new UserError('promiseNonNull');
|
||||
|
||||
$this->throwingData = [
|
||||
'sync' => function () {
|
||||
'sync' => function () {
|
||||
throw $this->syncError;
|
||||
},
|
||||
'syncNonNull' => function () {
|
||||
'syncNonNull' => function () {
|
||||
throw $this->syncNonNullError;
|
||||
},
|
||||
'promise' => function () {
|
||||
'promise' => function () {
|
||||
return new Deferred(function () {
|
||||
throw $this->promiseError;
|
||||
});
|
||||
},
|
||||
'promiseNonNull' => function () {
|
||||
'promiseNonNull' => function () {
|
||||
return new Deferred(function () {
|
||||
throw $this->promiseNonNullError;
|
||||
});
|
||||
},
|
||||
'syncNest' => function () {
|
||||
'syncNest' => function () {
|
||||
return $this->throwingData;
|
||||
},
|
||||
'syncNonNullNest' => function () {
|
||||
'syncNonNullNest' => function () {
|
||||
return $this->throwingData;
|
||||
},
|
||||
'promiseNest' => function () {
|
||||
'promiseNest' => function () {
|
||||
return new Deferred(function () {
|
||||
return $this->throwingData;
|
||||
});
|
||||
@ -74,29 +81,29 @@ class NonNullTest extends TestCase
|
||||
];
|
||||
|
||||
$this->nullingData = [
|
||||
'sync' => function () {
|
||||
'sync' => function () {
|
||||
return null;
|
||||
},
|
||||
'syncNonNull' => function () {
|
||||
'syncNonNull' => function () {
|
||||
return null;
|
||||
},
|
||||
'promise' => function () {
|
||||
'promise' => function () {
|
||||
return new Deferred(function () {
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'promiseNonNull' => function () {
|
||||
'promiseNonNull' => function () {
|
||||
return new Deferred(function () {
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'syncNest' => function () {
|
||||
'syncNest' => function () {
|
||||
return $this->nullingData;
|
||||
},
|
||||
'syncNonNullNest' => function () {
|
||||
'syncNonNullNest' => function () {
|
||||
return $this->nullingData;
|
||||
},
|
||||
'promiseNest' => function () {
|
||||
'promiseNest' => function () {
|
||||
return new Deferred(function () {
|
||||
return $this->nullingData;
|
||||
});
|
||||
@ -109,19 +116,19 @@ class NonNullTest extends TestCase
|
||||
];
|
||||
|
||||
$dataType = new ObjectType([
|
||||
'name' => 'DataType',
|
||||
'fields' => function() use (&$dataType) {
|
||||
'name' => 'DataType',
|
||||
'fields' => function () use (&$dataType) {
|
||||
return [
|
||||
'sync' => ['type' => Type::string()],
|
||||
'syncNonNull' => ['type' => Type::nonNull(Type::string())],
|
||||
'promise' => Type::string(),
|
||||
'promiseNonNull' => Type::nonNull(Type::string()),
|
||||
'syncNest' => $dataType,
|
||||
'syncNonNullNest' => Type::nonNull($dataType),
|
||||
'promiseNest' => $dataType,
|
||||
'sync' => ['type' => Type::string()],
|
||||
'syncNonNull' => ['type' => Type::nonNull(Type::string())],
|
||||
'promise' => Type::string(),
|
||||
'promiseNonNull' => Type::nonNull(Type::string()),
|
||||
'syncNest' => $dataType,
|
||||
'syncNonNullNest' => Type::nonNull($dataType),
|
||||
'promiseNest' => $dataType,
|
||||
'promiseNonNullNest' => Type::nonNull($dataType),
|
||||
];
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$this->schema = new Schema(['query' => $dataType]);
|
||||
@ -143,17 +150,18 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'sync' => null,
|
||||
],
|
||||
'data' => ['sync' => null],
|
||||
'errors' => [
|
||||
FormattedError::create(
|
||||
$this->syncError->getMessage(),
|
||||
[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
|
||||
@ -167,18 +175,19 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promise' => null,
|
||||
],
|
||||
'data' => ['promise' => null],
|
||||
'errors' => [
|
||||
FormattedError::create(
|
||||
$this->promiseError->getMessage(),
|
||||
[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
|
||||
@ -195,14 +204,15 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null
|
||||
],
|
||||
'data' => ['syncNest' => null],
|
||||
'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
|
||||
@ -218,15 +228,16 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null
|
||||
],
|
||||
'data' => ['syncNest' => null],
|
||||
'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
|
||||
@ -242,15 +253,16 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promiseNest' => null
|
||||
],
|
||||
'data' => ['promiseNest' => null],
|
||||
'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
|
||||
@ -266,15 +278,16 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promiseNest' => null
|
||||
],
|
||||
'data' => ['promiseNest' => null],
|
||||
'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()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,28 +327,28 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'data' => [
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
],
|
||||
@ -353,10 +366,13 @@ class NonNullTest extends TestCase
|
||||
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(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
|
||||
@ -413,10 +429,10 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null,
|
||||
'promiseNest' => null,
|
||||
'anotherNest' => null,
|
||||
'data' => [
|
||||
'syncNest' => null,
|
||||
'promiseNest' => null,
|
||||
'anotherNest' => null,
|
||||
'anotherPromiseNest' => null,
|
||||
],
|
||||
'errors' => [
|
||||
@ -424,10 +440,13 @@ class NonNullTest extends TestCase
|
||||
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(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
|
||||
@ -441,11 +460,12 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'sync' => null,
|
||||
]
|
||||
'data' => ['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
|
||||
@ -459,12 +479,13 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promise' => null,
|
||||
]
|
||||
'data' => ['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
|
||||
@ -480,17 +501,18 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null
|
||||
],
|
||||
'data' => ['syncNest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'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
|
||||
@ -506,15 +528,13 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null,
|
||||
],
|
||||
'data' => ['syncNest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||
'locations' => [['line' => 4, 'column' => 11]]
|
||||
'locations' => [['line' => 4, 'column' => 11]],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset(
|
||||
@ -536,15 +556,13 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promiseNest' => null,
|
||||
],
|
||||
'data' => ['promiseNest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
||||
'locations' => [['line' => 4, 'column' => 11]]
|
||||
'locations' => [['line' => 4, 'column' => 11]],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset(
|
||||
@ -566,15 +584,13 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'promiseNest' => null,
|
||||
],
|
||||
'data' => ['promiseNest' => null],
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||
'locations' => [['line' => 4, 'column' => 11]]
|
||||
'locations' => [['line' => 4, 'column' => 11]],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset(
|
||||
@ -618,31 +634,31 @@ class NonNullTest extends TestCase
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
]
|
||||
],
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
'syncNest' => [
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
],
|
||||
'promiseNest' => [
|
||||
'sync' => null,
|
||||
'sync' => null,
|
||||
'promise' => null,
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray();
|
||||
@ -703,18 +719,18 @@ class NonNullTest extends TestCase
|
||||
$ast = Parser::parse($doc);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'syncNest' => null,
|
||||
'promiseNest' => null,
|
||||
'anotherNest' => null,
|
||||
'data' => [
|
||||
'syncNest' => null,
|
||||
'promiseNest' => null,
|
||||
'anotherNest' => null,
|
||||
'anotherPromiseNest' => null,
|
||||
],
|
||||
'errors' => [
|
||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [ ['line' => 8, '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' => 41, 'column' => 19]]],
|
||||
]
|
||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 8, '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' => 41, 'column' => 19]]],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset(
|
||||
@ -734,10 +750,10 @@ class NonNullTest extends TestCase
|
||||
|
||||
$expected = [
|
||||
'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);
|
||||
}
|
||||
|
||||
@ -752,10 +768,13 @@ class NonNullTest extends TestCase
|
||||
$expected = [
|
||||
'errors' => [
|
||||
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
|
||||
@ -769,9 +788,9 @@ class NonNullTest extends TestCase
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
||||
'locations' => [['line' => 2, 'column' => 17]]
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
$this->assertArraySubset(
|
||||
$expected,
|
||||
@ -791,9 +810,9 @@ class NonNullTest extends TestCase
|
||||
'errors' => [
|
||||
[
|
||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||
'locations' => [['line' => 2, 'column' => 17]]
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertArraySubset(
|
||||
|
@ -1,17 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor\Promise;
|
||||
|
||||
|
||||
use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter;
|
||||
use GraphQL\Executor\Promise\Promise;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\FulfilledPromise;
|
||||
use React\Promise\LazyPromise;
|
||||
use React\Promise\Promise as ReactPromise;
|
||||
use React\Promise\RejectedPromise;
|
||||
use function class_exists;
|
||||
|
||||
/**
|
||||
* @group ReactPromise
|
||||
@ -20,19 +20,29 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
if(! class_exists('React\Promise\Promise')) {
|
||||
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
|
||||
if (class_exists('React\Promise\Promise')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
|
||||
}
|
||||
|
||||
public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void
|
||||
{
|
||||
$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 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(true));
|
||||
$this->assertSame(false, $reactAdapter->isThenable(1));
|
||||
@ -58,13 +68,16 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$reactPromise = new FulfilledPromise(1);
|
||||
$promise = $reactAdapter->convertThenable($reactPromise);
|
||||
$promise = $reactAdapter->convertThenable($reactPromise);
|
||||
|
||||
$result = null;
|
||||
|
||||
$resultPromise = $reactAdapter->then($promise, function ($value) use (&$result) {
|
||||
$result = $value;
|
||||
});
|
||||
$resultPromise = $reactAdapter->then(
|
||||
$promise,
|
||||
function ($value) use (&$result) {
|
||||
$result = $value;
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertSame(1, $result);
|
||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise);
|
||||
@ -73,9 +86,9 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
|
||||
public function testCreate() : void
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$resolvedPromise = $reactAdapter->create(function ($resolve) {
|
||||
$resolve(1);
|
||||
$resolve(1);
|
||||
});
|
||||
|
||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise);
|
||||
@ -84,7 +97,7 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
$result = null;
|
||||
|
||||
$resolvedPromise->then(function ($value) use (&$result) {
|
||||
$result = $value;
|
||||
$result = $value;
|
||||
});
|
||||
|
||||
$this->assertSame(1, $result);
|
||||
@ -92,7 +105,7 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
|
||||
public function testCreateFulfilled() : void
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$fulfilledPromise = $reactAdapter->createFulfilled(1);
|
||||
|
||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $fulfilledPromise);
|
||||
@ -109,7 +122,7 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
|
||||
public function testCreateRejected() : void
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise'));
|
||||
|
||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $rejectedPromise);
|
||||
@ -117,9 +130,12 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
|
||||
$exception = null;
|
||||
|
||||
$rejectedPromise->then(null, function ($error) use (&$exception) {
|
||||
$exception = $error;
|
||||
});
|
||||
$rejectedPromise->then(
|
||||
null,
|
||||
function ($error) use (&$exception) {
|
||||
$exception = $error;
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('\Exception', $exception);
|
||||
$this->assertEquals('I am a bad promise', $exception->getMessage());
|
||||
@ -128,7 +144,7 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
public function testAll() : void
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)];
|
||||
$promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)];
|
||||
|
||||
$allPromise = $reactAdapter->all($promises);
|
||||
|
||||
@ -138,7 +154,7 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
$result = null;
|
||||
|
||||
$allPromise->then(function ($values) use (&$result) {
|
||||
$result = $values;
|
||||
$result = $values;
|
||||
});
|
||||
|
||||
$this->assertSame([1, 2, 3], $result);
|
||||
@ -147,9 +163,9 @@ class ReactPromiseAdapterTest extends TestCase
|
||||
public function testAllShouldPreserveTheOrderOfTheArrayWhenResolvingAsyncPromises() : void
|
||||
{
|
||||
$reactAdapter = new ReactPromiseAdapter();
|
||||
$deferred = new Deferred();
|
||||
$promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)];
|
||||
$result = null;
|
||||
$deferred = new Deferred();
|
||||
$promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)];
|
||||
$result = null;
|
||||
|
||||
$reactAdapter->all($promises)->then(function ($values) use (&$result) {
|
||||
$result = $values;
|
||||
|
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor\Promise;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
@ -10,9 +13,7 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SyncPromiseAdapterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var SyncPromiseAdapter
|
||||
*/
|
||||
/** @var SyncPromiseAdapter */
|
||||
private $promises;
|
||||
|
||||
public function setUp()
|
||||
@ -22,7 +23,11 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
|
||||
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(true));
|
||||
$this->assertEquals(false, $this->promises->isThenable(1));
|
||||
@ -35,7 +40,8 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
|
||||
public function testConvert() : void
|
||||
{
|
||||
$dfd = new Deferred(function() {});
|
||||
$dfd = new Deferred(function () {
|
||||
});
|
||||
$result = $this->promises->convertThenable($dfd);
|
||||
|
||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result);
|
||||
@ -48,7 +54,8 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
|
||||
public function testThen() : void
|
||||
{
|
||||
$dfd = new Deferred(function() {});
|
||||
$dfd = new Deferred(function () {
|
||||
});
|
||||
$promise = $this->promises->convertThenable($dfd);
|
||||
|
||||
$result = $this->promises->then($promise);
|
||||
@ -59,18 +66,55 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
|
||||
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\Adapter\SyncPromise', $promise->adoptedPromise);
|
||||
|
||||
$promise = $this->promises->create(function($resolve, $reject) {
|
||||
$promise = $this->promises->create(function ($resolve, $reject) {
|
||||
$resolve('A');
|
||||
});
|
||||
|
||||
$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
|
||||
{
|
||||
$promise = $this->promises->createFulfilled('test');
|
||||
@ -94,8 +138,8 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
$promise1 = new SyncPromise();
|
||||
$promise2 = new SyncPromise();
|
||||
$promise3 = $promise2->then(
|
||||
function($value) {
|
||||
return $value .'-value3';
|
||||
function ($value) {
|
||||
return $value . '-value3';
|
||||
}
|
||||
);
|
||||
|
||||
@ -105,7 +149,7 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
new Promise($promise2, $this->promises),
|
||||
3,
|
||||
new Promise($promise3, $this->promises),
|
||||
[]
|
||||
[],
|
||||
];
|
||||
|
||||
$promise = $this->promises->all($data);
|
||||
@ -114,36 +158,46 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
$promise1->resolve('value1');
|
||||
$this->assertValidPromise($promise, null, null, SyncPromise::PENDING);
|
||||
$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
|
||||
{
|
||||
$called = [];
|
||||
|
||||
$deferred1 = new Deferred(function() use (&$called) {
|
||||
$deferred1 = new Deferred(function () use (&$called) {
|
||||
$called[] = 1;
|
||||
|
||||
return 1;
|
||||
});
|
||||
$deferred2 = new Deferred(function() use (&$called) {
|
||||
$deferred2 = new Deferred(function () use (&$called) {
|
||||
$called[] = 2;
|
||||
|
||||
return 2;
|
||||
});
|
||||
|
||||
$p1 = $this->promises->convertThenable($deferred1);
|
||||
$p2 = $this->promises->convertThenable($deferred2);
|
||||
|
||||
$p3 = $p2->then(function() use (&$called) {
|
||||
$dfd = new Deferred(function() use (&$called) {
|
||||
$p3 = $p2->then(function () use (&$called) {
|
||||
$dfd = new Deferred(function () use (&$called) {
|
||||
$called[] = 3;
|
||||
|
||||
return 3;
|
||||
});
|
||||
|
||||
return $this->promises->convertThenable($dfd);
|
||||
});
|
||||
|
||||
$p4 = $p3->then(function() use (&$called) {
|
||||
return new Deferred(function() use (&$called) {
|
||||
$p4 = $p3->then(function () use (&$called) {
|
||||
return new Deferred(function () use (&$called) {
|
||||
$called[] = 4;
|
||||
|
||||
return 4;
|
||||
});
|
||||
});
|
||||
@ -156,46 +210,10 @@ class SyncPromiseAdapterTest extends TestCase
|
||||
$this->assertEquals(SyncPromise::PENDING, $all->adoptedPromise->state);
|
||||
$this->assertEquals([1, 2], $called);
|
||||
|
||||
$expectedResult = [0,1,2,3,4];
|
||||
$result = $this->promises->wait($all);
|
||||
$expectedResult = [0, 1, 2, 3, 4];
|
||||
$result = $this->promises->wait($all);
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
$this->assertEquals([1, 2, 3, 4], $called);
|
||||
$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);
|
||||
$this->assertValidPromise($all, null, [0, 1, 2, 3, 4], SyncPromise::FULFILLED);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor\Promise;
|
||||
|
||||
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
||||
use PHPUnit\Framework\Error\Error;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function uniqid;
|
||||
|
||||
class SyncPromiseTest extends TestCase
|
||||
{
|
||||
public function getFulfilledPromiseResolveData()
|
||||
{
|
||||
$onFulfilledReturnsNull = function() {
|
||||
$onFulfilledReturnsNull = function () {
|
||||
return null;
|
||||
};
|
||||
$onFulfilledReturnsSameValue = function($value) {
|
||||
|
||||
$onFulfilledReturnsSameValue = function ($value) {
|
||||
return $value;
|
||||
};
|
||||
$onFulfilledReturnsOtherValue = function($value) {
|
||||
|
||||
$onFulfilledReturnsOtherValue = function ($value) {
|
||||
return 'other-' . $value;
|
||||
};
|
||||
$onFulfilledThrows = function($value) {
|
||||
throw new \Exception("onFulfilled throws this!");
|
||||
|
||||
$onFulfilledThrows = function ($value) {
|
||||
throw new \Exception('onFulfilled throws this!');
|
||||
};
|
||||
|
||||
return [
|
||||
@ -28,7 +35,7 @@ class SyncPromiseTest extends TestCase
|
||||
[uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED],
|
||||
['test-value', $onFulfilledReturnsSameValue, 'test-value', 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,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
@ -63,8 +69,7 @@ class SyncPromiseTest extends TestCase
|
||||
$expectedNextValue,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
@ -85,21 +90,27 @@ class SyncPromiseTest extends TestCase
|
||||
$expectedNextValue,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
$promise->resolve($resolvedValue);
|
||||
$this->assertEquals(SyncPromise::FULFILLED, $promise->state);
|
||||
|
||||
$nextPromise = $promise->then(null, function() {});
|
||||
$nextPromise = $promise->then(
|
||||
null,
|
||||
function () {
|
||||
}
|
||||
);
|
||||
$this->assertSame($promise, $nextPromise);
|
||||
|
||||
$onRejectedCalled = false;
|
||||
$nextPromise = $promise->then($onFulfilled, function () use (&$onRejectedCalled) {
|
||||
$onRejectedCalled = true;
|
||||
});
|
||||
$nextPromise = $promise->then(
|
||||
$onFulfilled,
|
||||
function () use (&$onRejectedCalled) {
|
||||
$onRejectedCalled = true;
|
||||
}
|
||||
);
|
||||
|
||||
if ($onFulfilled) {
|
||||
$this->assertNotSame($promise, $nextPromise);
|
||||
@ -124,19 +135,57 @@ class SyncPromiseTest extends TestCase
|
||||
$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()
|
||||
{
|
||||
$onRejectedReturnsNull = function() {
|
||||
$onRejectedReturnsNull = function () {
|
||||
return null;
|
||||
};
|
||||
$onRejectedReturnsSomeValue = function($reason) {
|
||||
|
||||
$onRejectedReturnsSomeValue = function ($reason) {
|
||||
return 'some-value';
|
||||
};
|
||||
$onRejectedThrowsSameReason = function($reason) {
|
||||
|
||||
$onRejectedThrowsSameReason = function ($reason) {
|
||||
throw $reason;
|
||||
};
|
||||
$onRejectedThrowsOtherReason = function($value) {
|
||||
throw new \Exception("onRejected throws other!");
|
||||
|
||||
$onRejectedThrowsOtherReason = function ($value) {
|
||||
throw new \Exception('onRejected throws other!');
|
||||
};
|
||||
|
||||
return [
|
||||
@ -158,8 +207,7 @@ class SyncPromiseTest extends TestCase
|
||||
$expectedNextValue,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
@ -169,7 +217,6 @@ class SyncPromiseTest extends TestCase
|
||||
$this->expectException(\Throwable::class);
|
||||
$this->expectExceptionMessage('Cannot change rejection reason');
|
||||
$promise->reject(new \Exception('other-reason'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,8 +228,7 @@ class SyncPromiseTest extends TestCase
|
||||
$expectedNextValue,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
@ -203,8 +249,7 @@ class SyncPromiseTest extends TestCase
|
||||
$expectedNextValue,
|
||||
$expectedNextReason,
|
||||
$expectedNextState
|
||||
)
|
||||
{
|
||||
) {
|
||||
$promise = new SyncPromise();
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
|
||||
@ -214,22 +259,26 @@ class SyncPromiseTest extends TestCase
|
||||
try {
|
||||
$promise->reject(new \Exception('other-reason'));
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->assertEquals('Cannot change rejection reason', $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$promise->resolve('anything');
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->assertEquals('Cannot resolve rejected promise', $e->getMessage());
|
||||
}
|
||||
|
||||
$nextPromise = $promise->then(function() {}, null);
|
||||
$nextPromise = $promise->then(
|
||||
function () {
|
||||
},
|
||||
null
|
||||
);
|
||||
$this->assertSame($promise, $nextPromise);
|
||||
|
||||
$onFulfilledCalled = false;
|
||||
$nextPromise = $promise->then(
|
||||
$nextPromise = $promise->then(
|
||||
function () use (&$onFulfilledCalled) {
|
||||
$onFulfilledCalled = true;
|
||||
},
|
||||
@ -266,7 +315,7 @@ class SyncPromiseTest extends TestCase
|
||||
try {
|
||||
$promise->resolve($promise);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->assertEquals('Cannot resolve promise with self', $e->getMessage());
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
}
|
||||
@ -299,24 +348,25 @@ class SyncPromiseTest extends TestCase
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||
}
|
||||
|
||||
$promise->reject(new \Exception("Rejected Reason"));
|
||||
$this->assertValidPromise($promise, "Rejected Reason", null, SyncPromise::REJECTED);
|
||||
$promise->reject(new \Exception('Rejected Reason'));
|
||||
$this->assertValidPromise($promise, 'Rejected Reason', null, SyncPromise::REJECTED);
|
||||
|
||||
$promise = new SyncPromise();
|
||||
$promise2 = $promise->then(null, function() {
|
||||
return 'value';
|
||||
});
|
||||
$promise->reject(new \Exception("Rejected Again"));
|
||||
$promise = new SyncPromise();
|
||||
$promise2 = $promise->then(
|
||||
null,
|
||||
function () {
|
||||
return 'value';
|
||||
}
|
||||
);
|
||||
$promise->reject(new \Exception('Rejected Again'));
|
||||
$this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED);
|
||||
|
||||
$promise = new SyncPromise();
|
||||
$promise = new SyncPromise();
|
||||
$promise2 = $promise->then();
|
||||
$promise->reject(new \Exception("Rejected Once Again"));
|
||||
$this->assertValidPromise($promise2, "Rejected Once Again", null, SyncPromise::REJECTED);
|
||||
$promise->reject(new \Exception('Rejected Once Again'));
|
||||
$this->assertValidPromise($promise2, 'Rejected Once Again', null, SyncPromise::REJECTED);
|
||||
}
|
||||
|
||||
public function testPendingPromiseThen() : void
|
||||
@ -331,12 +381,14 @@ class SyncPromiseTest extends TestCase
|
||||
|
||||
// Make sure that it queues derivative promises until resolution:
|
||||
$onFulfilledCount = 0;
|
||||
$onRejectedCount = 0;
|
||||
$onFulfilled = function($value) use (&$onFulfilledCount) {
|
||||
$onRejectedCount = 0;
|
||||
$onFulfilled = function ($value) use (&$onFulfilledCount) {
|
||||
$onFulfilledCount++;
|
||||
|
||||
return $onFulfilledCount;
|
||||
};
|
||||
$onRejected = function($reason) use (&$onRejectedCount) {
|
||||
|
||||
$onRejected = function ($reason) use (&$onRejectedCount) {
|
||||
$onRejectedCount++;
|
||||
throw $reason;
|
||||
};
|
||||
@ -367,35 +419,4 @@ class SyncPromiseTest extends TestCase
|
||||
$this->assertValidPromise($nextPromise3, null, 2, 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);
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\GraphQL;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Tests\Executor\TestClasses\Adder;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
|
||||
require_once __DIR__ . '/TestClasses.php';
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function json_encode;
|
||||
use function uniqid;
|
||||
|
||||
class ResolveTest extends TestCase
|
||||
{
|
||||
// Execute: resolve function
|
||||
|
||||
private function buildSchema($testField)
|
||||
{
|
||||
return new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'test' => $testField
|
||||
]
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('default function accesses properties')
|
||||
*/
|
||||
@ -32,9 +24,7 @@ class ResolveTest extends TestCase
|
||||
{
|
||||
$schema = $this->buildSchema(['type' => Type::string()]);
|
||||
|
||||
$source = [
|
||||
'test' => 'testValue'
|
||||
];
|
||||
$source = ['test' => 'testValue'];
|
||||
|
||||
$this->assertEquals(
|
||||
['data' => ['test' => 'testValue']],
|
||||
@ -42,18 +32,28 @@ 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')
|
||||
*/
|
||||
public function testDefaultFunctionCallsClosures() : void
|
||||
{
|
||||
$schema = $this->buildSchema(['type' => Type::string()]);
|
||||
$schema = $this->buildSchema(['type' => Type::string()]);
|
||||
$_secret = 'secretValue' . uniqid();
|
||||
|
||||
$source = [
|
||||
'test' => function() use ($_secret) {
|
||||
'test' => function () use ($_secret) {
|
||||
return $_secret;
|
||||
}
|
||||
},
|
||||
];
|
||||
$this->assertEquals(
|
||||
['data' => ['test' => $_secret]],
|
||||
@ -69,7 +69,7 @@ class ResolveTest extends TestCase
|
||||
$schema = $this->buildSchema([
|
||||
'type' => Type::int(),
|
||||
'args' => [
|
||||
'addend1' => [ 'type' => Type::int() ],
|
||||
'addend1' => ['type' => Type::int()],
|
||||
],
|
||||
]);
|
||||
|
||||
@ -85,14 +85,14 @@ class ResolveTest extends TestCase
|
||||
public function testUsesProvidedResolveFunction() : void
|
||||
{
|
||||
$schema = $this->buildSchema([
|
||||
'type' => Type::string(),
|
||||
'args' => [
|
||||
'type' => Type::string(),
|
||||
'args' => [
|
||||
'aStr' => ['type' => Type::string()],
|
||||
'aInt' => ['type' => Type::int()],
|
||||
],
|
||||
'resolve' => function ($source, $args) {
|
||||
return json_encode([$source, $args]);
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$this->assertEquals(
|
||||
|
@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\Error\Error;
|
||||
use GraphQL\Error\FormattedError;
|
||||
use GraphQL\Executor\ExecutionResult;
|
||||
use GraphQL\Executor\Executor;
|
||||
@ -29,37 +31,38 @@ class SyncTest extends TestCase
|
||||
public function setUp()
|
||||
{
|
||||
$this->schema = new Schema([
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'query' => new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'syncField' => [
|
||||
'type' => Type::string(),
|
||||
'syncField' => [
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($rootValue) {
|
||||
return $rootValue;
|
||||
}
|
||||
},
|
||||
],
|
||||
'asyncField' => [
|
||||
'type' => Type::string(),
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($rootValue) {
|
||||
return new Deferred(function () use ($rootValue) {
|
||||
return $rootValue;
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
'mutation' => new ObjectType([
|
||||
'name' => 'Mutation',
|
||||
'name' => 'Mutation',
|
||||
'fields' => [
|
||||
'syncMutationField' => [
|
||||
'type' => Type::string(),
|
||||
'type' => Type::string(),
|
||||
'resolve' => function ($rootValue) {
|
||||
return $rootValue;
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
]);
|
||||
|
||||
$this->promiseAdapter = new SyncPromiseAdapter();
|
||||
}
|
||||
|
||||
@ -70,7 +73,7 @@ class SyncTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForInitialErrors() : void
|
||||
{
|
||||
$doc = 'fragment Example on Query { syncField }';
|
||||
$doc = 'fragment Example on Query { syncField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
@ -79,106 +82,6 @@ class SyncTest extends TestCase
|
||||
$this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise if fields are all synchronous')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void
|
||||
{
|
||||
$doc = 'query Example { syncField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise if mutation fields are all synchronous')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void
|
||||
{
|
||||
$doc = 'mutation Example { syncMutationField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('returns a Promise if any field is asynchronous')
|
||||
*/
|
||||
public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void
|
||||
{
|
||||
$doc = 'query Example { syncField, asyncField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
// Describe: graphqlSync
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for syntax errors')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForSyntaxErrors() : void
|
||||
{
|
||||
$doc = 'fragment Example on Query { { { syncField }';
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc
|
||||
);
|
||||
$this->assertSync([
|
||||
'errors' => [
|
||||
['message' => 'Syntax Error: Expected Name, found {',
|
||||
'locations' => [['line' => 1, 'column' => 29]]]
|
||||
]
|
||||
], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for validation errors')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForValidationErrors() : void
|
||||
{
|
||||
$doc = 'fragment Example on Query { unknownField }';
|
||||
$validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc));
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc
|
||||
);
|
||||
$expected = [
|
||||
'errors' => Utils::map($validationErrors, function ($e) {
|
||||
return FormattedError::createFromException($e);
|
||||
})
|
||||
];
|
||||
$this->assertSync($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for sync execution')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForSyncExecution() : void
|
||||
{
|
||||
$doc = 'query Example { syncField }';
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc,
|
||||
'rootValue'
|
||||
);
|
||||
$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);
|
||||
@ -191,7 +94,56 @@ class SyncTest extends TestCase
|
||||
$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);
|
||||
$this->assertArraySubset(
|
||||
$expectedFinalArray,
|
||||
$actualResult->adoptedPromise->result->toArray(),
|
||||
false,
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise if fields are all synchronous')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void
|
||||
{
|
||||
$doc = 'query Example { syncField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
// Describe: graphqlSync
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise if mutation fields are all synchronous')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void
|
||||
{
|
||||
$doc = 'mutation Example { syncMutationField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('returns a Promise if any field is asynchronous')
|
||||
*/
|
||||
public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void
|
||||
{
|
||||
$doc = 'query Example { syncField, asyncField }';
|
||||
$result = $this->execute(
|
||||
$this->schema,
|
||||
Parser::parse($doc),
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result);
|
||||
}
|
||||
|
||||
private function assertAsync($expectedFinalArray, $actualResult)
|
||||
@ -202,6 +154,70 @@ class SyncTest extends TestCase
|
||||
$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);
|
||||
$this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), false, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for syntax errors')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForSyntaxErrors() : void
|
||||
{
|
||||
$doc = 'fragment Example on Query { { { syncField }';
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc
|
||||
);
|
||||
$this->assertSync(
|
||||
[
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'Syntax Error: Expected Name, found {',
|
||||
'locations' => [['line' => 1, 'column' => 29]],
|
||||
],
|
||||
],
|
||||
],
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
private function graphqlSync($schema, $doc, $rootValue = null)
|
||||
{
|
||||
return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for validation errors')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForValidationErrors() : void
|
||||
{
|
||||
$doc = 'fragment Example on Query { unknownField }';
|
||||
$validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc));
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc
|
||||
);
|
||||
$expected = [
|
||||
'errors' => Utils::map(
|
||||
$validationErrors,
|
||||
function ($e) {
|
||||
return FormattedError::createFromException($e);
|
||||
}
|
||||
),
|
||||
];
|
||||
$this->assertSync($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see it('does not return a Promise for sync execution')
|
||||
*/
|
||||
public function testDoesNotReturnAPromiseForSyncExecution() : void
|
||||
{
|
||||
$doc = 'query Example { syncField }';
|
||||
$result = $this->graphqlSync(
|
||||
$this->schema,
|
||||
$doc,
|
||||
'rootValue'
|
||||
);
|
||||
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
||||
}
|
||||
}
|
||||
|
@ -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'];
|
||||
};
|
||||
}
|
||||
}
|
23
tests/Executor/TestClasses/Adder.php
Normal file
23
tests/Executor/TestClasses/Adder.php
Normal 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'];
|
||||
};
|
||||
}
|
||||
}
|
20
tests/Executor/TestClasses/Cat.php
Normal file
20
tests/Executor/TestClasses/Cat.php
Normal 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;
|
||||
}
|
||||
}
|
56
tests/Executor/TestClasses/ComplexScalar.php
Normal file
56
tests/Executor/TestClasses/ComplexScalar.php
Normal 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));
|
||||
}
|
||||
}
|
20
tests/Executor/TestClasses/Dog.php
Normal file
20
tests/Executor/TestClasses/Dog.php
Normal 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;
|
||||
}
|
||||
}
|
16
tests/Executor/TestClasses/Human.php
Normal file
16
tests/Executor/TestClasses/Human.php
Normal 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;
|
||||
}
|
||||
}
|
19
tests/Executor/TestClasses/NotSpecial.php
Normal file
19
tests/Executor/TestClasses/NotSpecial.php
Normal 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;
|
||||
}
|
||||
}
|
16
tests/Executor/TestClasses/NumberHolder.php
Normal file
16
tests/Executor/TestClasses/NumberHolder.php
Normal 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;
|
||||
}
|
||||
}
|
28
tests/Executor/TestClasses/Person.php
Normal file
28
tests/Executor/TestClasses/Person.php
Normal 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;
|
||||
}
|
||||
}
|
44
tests/Executor/TestClasses/Root.php
Normal file
44
tests/Executor/TestClasses/Root.php
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
19
tests/Executor/TestClasses/Special.php
Normal file
19
tests/Executor/TestClasses/Special.php
Normal 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;
|
||||
}
|
||||
}
|
@ -1,64 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
require_once __DIR__ . '/TestClasses.php';
|
||||
|
||||
use GraphQL\Error\Warning;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\GraphQL;
|
||||
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\ObjectType;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UnionInterfaceTest extends TestCase
|
||||
{
|
||||
/** @var */
|
||||
public $schema;
|
||||
|
||||
/** @var Cat */
|
||||
public $garfield;
|
||||
|
||||
/** @var Dog */
|
||||
public $odie;
|
||||
|
||||
/** @var Person */
|
||||
public $liz;
|
||||
|
||||
/** @var Person */
|
||||
public $john;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$NamedType = new InterfaceType([
|
||||
'name' => 'Named',
|
||||
'name' => 'Named',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$NamedType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
],
|
||||
'isTypeOf' => function ($value) {
|
||||
'isTypeOf' => function ($value) {
|
||||
return $value instanceof Dog;
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$NamedType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
],
|
||||
'isTypeOf' => function ($value) {
|
||||
'isTypeOf' => function ($value) {
|
||||
return $value instanceof Cat;
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'types' => [$DogType, $CatType],
|
||||
'name' => 'Pet',
|
||||
'types' => [$DogType, $CatType],
|
||||
'resolveType' => function ($value) use ($DogType, $CatType) {
|
||||
if ($value instanceof Dog) {
|
||||
return $DogType;
|
||||
@ -66,32 +78,31 @@ class UnionInterfaceTest extends TestCase
|
||||
if ($value instanceof Cat) {
|
||||
return $CatType;
|
||||
}
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$PersonType = new ObjectType([
|
||||
'name' => 'Person',
|
||||
'name' => 'Person',
|
||||
'interfaces' => [$NamedType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'pets' => ['type' => Type::listOf($PetType)],
|
||||
'friends' => ['type' => Type::listOf($NamedType)]
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'pets' => ['type' => Type::listOf($PetType)],
|
||||
'friends' => ['type' => Type::listOf($NamedType)],
|
||||
],
|
||||
'isTypeOf' => function ($value) {
|
||||
'isTypeOf' => function ($value) {
|
||||
return $value instanceof Person;
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$this->schema = new Schema([
|
||||
'query' => $PersonType,
|
||||
'types' => [ $PetType ]
|
||||
'types' => [$PetType],
|
||||
]);
|
||||
|
||||
$this->garfield = new Cat('Garfield', false);
|
||||
$this->odie = new Dog('Odie', true);
|
||||
$this->liz = new Person('Liz');
|
||||
$this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]);
|
||||
|
||||
$this->odie = new Dog('Odie', true);
|
||||
$this->liz = new Person('Liz');
|
||||
$this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]);
|
||||
}
|
||||
|
||||
// Execute: Union and intersection types
|
||||
@ -101,7 +112,6 @@ class UnionInterfaceTest extends TestCase
|
||||
*/
|
||||
public function testCanIntrospectOnUnionAndIntersectionTypes() : void
|
||||
{
|
||||
|
||||
$ast = Parser::parse('
|
||||
{
|
||||
Named: __type(name: "Named") {
|
||||
@ -128,33 +138,33 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'Named' => [
|
||||
'kind' => 'INTERFACE',
|
||||
'name' => 'Named',
|
||||
'fields' => [
|
||||
['name' => 'name']
|
||||
'kind' => 'INTERFACE',
|
||||
'name' => 'Named',
|
||||
'fields' => [
|
||||
['name' => 'name'],
|
||||
],
|
||||
'interfaces' => null,
|
||||
'interfaces' => null,
|
||||
'possibleTypes' => [
|
||||
['name' => 'Person'],
|
||||
['name' => 'Dog'],
|
||||
['name' => 'Cat']
|
||||
['name' => 'Cat'],
|
||||
],
|
||||
'enumValues' => null,
|
||||
'inputFields' => null
|
||||
'enumValues' => null,
|
||||
'inputFields' => null,
|
||||
],
|
||||
'Pet' => [
|
||||
'kind' => 'UNION',
|
||||
'name' => 'Pet',
|
||||
'fields' => null,
|
||||
'interfaces' => null,
|
||||
'Pet' => [
|
||||
'kind' => 'UNION',
|
||||
'name' => 'Pet',
|
||||
'fields' => null,
|
||||
'interfaces' => null,
|
||||
'possibleTypes' => [
|
||||
['name' => 'Dog'],
|
||||
['name' => 'Cat']
|
||||
['name' => 'Cat'],
|
||||
],
|
||||
'enumValues' => null,
|
||||
'inputFields' => null
|
||||
]
|
||||
]
|
||||
'enumValues' => null,
|
||||
'inputFields' => null,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray());
|
||||
}
|
||||
@ -165,7 +175,7 @@ class UnionInterfaceTest extends TestCase
|
||||
public function testExecutesUsingUnionTypes() : void
|
||||
{
|
||||
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
||||
$ast = Parser::parse('
|
||||
$ast = Parser::parse('
|
||||
{
|
||||
__typename
|
||||
name
|
||||
@ -180,12 +190,12 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'__typename' => 'Person',
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
['__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());
|
||||
@ -197,7 +207,7 @@ class UnionInterfaceTest extends TestCase
|
||||
public function testExecutesUnionTypesWithInlineFragments() : void
|
||||
{
|
||||
// This is the valid version of the query in the above test.
|
||||
$ast = Parser::parse('
|
||||
$ast = Parser::parse('
|
||||
{
|
||||
__typename
|
||||
name
|
||||
@ -217,13 +227,13 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'__typename' => 'Person',
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
['__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());
|
||||
}
|
||||
@ -234,7 +244,7 @@ class UnionInterfaceTest extends TestCase
|
||||
public function testExecutesUsingInterfaceTypes() : void
|
||||
{
|
||||
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
||||
$ast = Parser::parse('
|
||||
$ast = Parser::parse('
|
||||
{
|
||||
__typename
|
||||
name
|
||||
@ -249,12 +259,12 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'__typename' => 'Person',
|
||||
'name' => 'John',
|
||||
'friends' => [
|
||||
'name' => 'John',
|
||||
'friends' => [
|
||||
['__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());
|
||||
@ -266,7 +276,7 @@ class UnionInterfaceTest extends TestCase
|
||||
public function testExecutesInterfaceTypesWithInlineFragments() : void
|
||||
{
|
||||
// This is the valid version of the query in the above test.
|
||||
$ast = Parser::parse('
|
||||
$ast = Parser::parse('
|
||||
{
|
||||
__typename
|
||||
name
|
||||
@ -285,12 +295,12 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'__typename' => 'Person',
|
||||
'name' => 'John',
|
||||
'friends' => [
|
||||
'name' => 'John',
|
||||
'friends' => [
|
||||
['__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));
|
||||
@ -336,16 +346,16 @@ class UnionInterfaceTest extends TestCase
|
||||
$expected = [
|
||||
'data' => [
|
||||
'__typename' => 'Person',
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
'name' => 'John',
|
||||
'pets' => [
|
||||
['__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' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
||||
]
|
||||
]
|
||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||
@ -356,36 +366,45 @@ class UnionInterfaceTest extends TestCase
|
||||
*/
|
||||
public function testGetsExecutionInfoInResolver() : void
|
||||
{
|
||||
$encounteredContext = null;
|
||||
$encounteredSchema = null;
|
||||
$encounteredContext = null;
|
||||
$encounteredSchema = null;
|
||||
$encounteredRootValue = null;
|
||||
$PersonType2 = null;
|
||||
$PersonType2 = null;
|
||||
|
||||
$NamedType2 = new InterfaceType([
|
||||
'name' => 'Named',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
'name' => 'Named',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
],
|
||||
'resolveType' => function ($obj, $context, ResolveInfo $info) use (&$encounteredContext, &$encounteredSchema, &$encounteredRootValue, &$PersonType2) {
|
||||
$encounteredContext = $context;
|
||||
$encounteredSchema = $info->schema;
|
||||
'resolveType' => function (
|
||||
$obj,
|
||||
$context,
|
||||
ResolveInfo $info
|
||||
) use (
|
||||
&$encounteredContext,
|
||||
&
|
||||
$encounteredSchema,
|
||||
&$encounteredRootValue,
|
||||
&$PersonType2
|
||||
) {
|
||||
$encounteredContext = $context;
|
||||
$encounteredSchema = $info->schema;
|
||||
$encounteredRootValue = $info->rootValue;
|
||||
|
||||
return $PersonType2;
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
$PersonType2 = new ObjectType([
|
||||
'name' => 'Person',
|
||||
'name' => 'Person',
|
||||
'interfaces' => [$NamedType2],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'friends' => ['type' => Type::listOf($NamedType2)],
|
||||
],
|
||||
]);
|
||||
|
||||
$schema2 = new Schema([
|
||||
'query' => $PersonType2
|
||||
]);
|
||||
$schema2 = new Schema(['query' => $PersonType2]);
|
||||
|
||||
$john2 = new Person('John', [], [$this->liz]);
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Executor\Values;
|
||||
@ -10,19 +13,97 @@ use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function count;
|
||||
use function var_export;
|
||||
use const PHP_EOL;
|
||||
|
||||
class ValuesTest extends TestCase
|
||||
{
|
||||
/** @var Schema */
|
||||
private static $schema;
|
||||
|
||||
public function testGetIDVariableValues() : void
|
||||
{
|
||||
$this->expectInputVariablesMatchOutputVariables(['idInput' => '123456789']);
|
||||
$this->assertEquals(
|
||||
['errors'=> [], 'coerced' => ['idInput' => '123456789']],
|
||||
self::runTestCase(['idInput' => 123456789]),
|
||||
['errors' => [], 'coerced' => ['idInput' => '123456789']],
|
||||
$this->runTestCase(['idInput' => 123456789]),
|
||||
'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
|
||||
{
|
||||
$this->expectInputVariablesMatchOutputVariables(['boolInput' => true]);
|
||||
@ -64,11 +145,20 @@ class ValuesTest extends TestCase
|
||||
$this->expectGraphQLError(['idInput' => true]);
|
||||
}
|
||||
|
||||
private function expectGraphQLError($variables) : void
|
||||
{
|
||||
$result = $this->runTestCase($variables);
|
||||
$this->assertGreaterThan(0, count($result['errors']));
|
||||
}
|
||||
|
||||
public function testFloatForIDVariableThrowsError() : void
|
||||
{
|
||||
$this->expectGraphQLError(['idInput' => 1.0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for running test cases and making assertions
|
||||
*/
|
||||
public function testStringForBooleanVariableThrowsError() : void
|
||||
{
|
||||
$this->expectGraphQLError(['boolInput' => 'true']);
|
||||
@ -98,77 +188,4 @@ class ValuesTest extends TestCase
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
<?php
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
require_once __DIR__ . '/TestClasses.php';
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Error\Error;
|
||||
use GraphQL\Executor\Executor;
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Type\Schema;
|
||||
use GraphQL\Tests\Executor\TestClasses\ComplexScalar;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function json_encode;
|
||||
|
||||
/**
|
||||
* Execute: Handles inputs
|
||||
@ -18,7 +21,6 @@ use PHPUnit\Framework\TestCase;
|
||||
*/
|
||||
class VariablesTest extends TestCase
|
||||
{
|
||||
|
||||
public function testUsingInlineStructs() : void
|
||||
{
|
||||
// executes with complex input:
|
||||
@ -29,14 +31,12 @@ class VariablesTest extends TestCase
|
||||
');
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'
|
||||
]
|
||||
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// properly parses single value to list:
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
|
||||
}
|
||||
@ -46,7 +46,7 @@ class VariablesTest extends TestCase
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// properly parses null value to null
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
|
||||
}
|
||||
@ -56,7 +56,7 @@ class VariablesTest extends TestCase
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// properly parses null value in list
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
|
||||
}
|
||||
@ -73,12 +73,13 @@ class VariablesTest extends TestCase
|
||||
');
|
||||
|
||||
$expected = [
|
||||
'data' => ['fieldWithObjectInput' => null],
|
||||
'data' => ['fieldWithObjectInput' => null],
|
||||
'errors' => [[
|
||||
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
|
||||
'path' => ['fieldWithObjectInput'],
|
||||
'locations' => [['line' => 3, 'column' => 39]]
|
||||
]]
|
||||
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
|
||||
'path' => ['fieldWithObjectInput'],
|
||||
'locations' => [['line' => 3, 'column' => 39]],
|
||||
],
|
||||
],
|
||||
];
|
||||
$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
|
||||
{
|
||||
$doc = '
|
||||
@ -119,7 +191,7 @@ class VariablesTest extends TestCase
|
||||
');
|
||||
|
||||
$expected = [
|
||||
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']
|
||||
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
@ -132,63 +204,61 @@ class VariablesTest extends TestCase
|
||||
);
|
||||
|
||||
// executes with complex scalar input:
|
||||
$params = [ 'input' => [ 'c' => 'foo', 'd' => 'SerializedValue' ] ];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$params = ['input' => ['c' => 'foo', 'd' => 'SerializedValue']];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$expected = [
|
||||
'data' => [
|
||||
'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'
|
||||
]
|
||||
'data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// errors on null for nested non-null:
|
||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value ' .
|
||||
'{"a":"foo","b":"bar","c":null}; ' .
|
||||
'Expected non-nullable type String! not to be null at value.c.',
|
||||
'locations' => [['line' => 2, 'column' => 21]],
|
||||
'category' => 'graphql'
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// errors on incorrect type:
|
||||
$params = [ 'input' => 'foo bar' ];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$params = ['input' => 'foo bar'];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value "foo bar"; ' .
|
||||
'Expected type TestInputObject to be an object.',
|
||||
'locations' => [ [ 'line' => 2, 'column' => 21 ] ],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 2, 'column' => 21]],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
// errors on omission of nested non-null:
|
||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar']];
|
||||
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; '.
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' .
|
||||
'Field value.c of required type String! was not provided.',
|
||||
'locations' => [['line' => 2, 'column' => 21]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
@ -198,62 +268,59 @@ class VariablesTest extends TestCase
|
||||
fieldWithNestedObjectInput(input: $input)
|
||||
}
|
||||
';
|
||||
$params = [ 'input' => [ 'na' => [ 'a' => 'foo' ] ] ];
|
||||
$params = ['input' => ['na' => ['a' => 'foo']]];
|
||||
|
||||
$result = $this->executeQuery($nestedDoc, $params);
|
||||
$result = $this->executeQuery($nestedDoc, $params);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
||||
'Field value.na.c of required type String! was not provided.',
|
||||
'locations' => [['line' => 2, 'column' => 19]],
|
||||
'category' => 'graphql',
|
||||
'category' => 'graphql',
|
||||
],
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
||||
'Field value.nb of required type String! was not provided.',
|
||||
'locations' => [['line' => 2, 'column' => 19]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
|
||||
|
||||
// errors on addition of unknown input field
|
||||
$params = ['input' => [ 'a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog' ]];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog']];
|
||||
$result = $this->executeQuery($doc, $params);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value ' .
|
||||
'{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
|
||||
'Field "extra" is not defined by type TestInputObject.',
|
||||
'locations' => [['line' => 2, 'column' => 21]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
// Describe: Handles nullable scalars
|
||||
|
||||
/**
|
||||
* @see it('allows nullable inputs to be omitted')
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeOmitted() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithNullableStringInput
|
||||
}
|
||||
');
|
||||
$expected = [
|
||||
'data' => ['fieldWithNullableStringInput' => null]
|
||||
'data' => ['fieldWithNullableStringInput' => null],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
@ -264,7 +331,7 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeOmittedInAVariable() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
query SetsNullable($value: String) {
|
||||
fieldWithNullableStringInput(input: $value)
|
||||
}
|
||||
@ -279,7 +346,7 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
query SetsNullable {
|
||||
fieldWithNullableStringInput(input: $value)
|
||||
}
|
||||
@ -288,12 +355,15 @@ class VariablesTest extends TestCase
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
|
||||
// Describe: Handles non-nullable scalars
|
||||
|
||||
/**
|
||||
* @see it('allows nullable inputs to be set to null in a variable')
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeSetToNullInAVariable() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
query SetsNullable($value: String) {
|
||||
fieldWithNullableStringInput(input: $value)
|
||||
}
|
||||
@ -308,12 +378,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeSetToAValueInAVariable() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query SetsNullable($value: String) {
|
||||
fieldWithNullableStringInput(input: $value)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||
$expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -323,7 +393,7 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNullableInputsToBeSetToAValueDirectly() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithNullableStringInput(input: "a")
|
||||
}
|
||||
@ -332,21 +402,18 @@ class VariablesTest extends TestCase
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
|
||||
// Describe: Handles non-nullable scalars
|
||||
|
||||
/**
|
||||
* @see it('allows non-nullable inputs to be omitted given a default')
|
||||
*/
|
||||
public function testAllowsNonNullableInputsToBeOmittedGivenADefault() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
query SetsNonNullable($value: String = "default") {
|
||||
fieldWithNonNullableStringInput(input: $value)
|
||||
}
|
||||
');
|
||||
$expected = [
|
||||
'data' => ['fieldWithNonNullableStringInput' => '"default"']
|
||||
'data' => ['fieldWithNonNullableStringInput' => '"default"'],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -365,11 +432,11 @@ class VariablesTest extends TestCase
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'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]],
|
||||
'category' => 'graphql'
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -379,22 +446,22 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query SetsNonNullable($value: String!) {
|
||||
fieldWithNonNullableStringInput(input: $value)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['value' => null]);
|
||||
$result = $this->executeQuery($doc, ['value' => null]);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$value" got invalid value null; ' .
|
||||
'Expected non-nullable type String! not to be null.',
|
||||
'locations' => [['line' => 2, 'column' => 31]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -404,12 +471,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNonNullableInputsToBeSetToAValueInAVariable() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query SetsNonNullable($value: String!) {
|
||||
fieldWithNonNullableStringInput(input: $value)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||
$expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -419,7 +486,7 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNonNullableInputsToBeSetToAValueDirectly() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithNonNullableStringInput(input: "a")
|
||||
}
|
||||
@ -433,47 +500,50 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testReportsErrorForMissingNonNullableInputs() : void
|
||||
{
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithNonNullableStringInput
|
||||
}
|
||||
');
|
||||
$expected = [
|
||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||
'errors' => [[
|
||||
'message' => 'Argument "input" of required type "String!" was not provided.',
|
||||
'locations' => [ [ 'line' => 3, 'column' => 9 ] ],
|
||||
'path' => [ 'fieldWithNonNullableStringInput' ],
|
||||
'category' => 'graphql',
|
||||
]]
|
||||
'message' => 'Argument "input" of required type "String!" was not provided.',
|
||||
'locations' => [['line' => 3, 'column' => 9]],
|
||||
'path' => ['fieldWithNonNullableStringInput'],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
// Describe: Handles lists and nullability
|
||||
|
||||
/**
|
||||
* @see it('reports error for array passed into string input')
|
||||
*/
|
||||
public function testReportsErrorForArrayPassedIntoStringInput() : void
|
||||
{
|
||||
|
||||
$doc = '
|
||||
$doc = '
|
||||
query SetsNonNullable($value: String!) {
|
||||
fieldWithNonNullableStringInput(input: $value)
|
||||
}
|
||||
';
|
||||
$variables = ['value' => [1, 2, 3]];
|
||||
$result = $this->executeQuery($doc, $variables);
|
||||
$result = $this->executeQuery($doc, $variables);
|
||||
|
||||
$expected = [
|
||||
'errors' => [[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$value" got invalid value [1,2,3]; Expected type ' .
|
||||
'String; String cannot represent an array value: [1,2,3]',
|
||||
'category' => 'graphql',
|
||||
'category' => 'graphql',
|
||||
'locations' => [
|
||||
['line' => 2, 'column' => 31]
|
||||
]
|
||||
]]
|
||||
['line' => 2, 'column' => 31],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -498,38 +568,37 @@ class VariablesTest extends TestCase
|
||||
// and are being run against a new schema which have introduced a breaking
|
||||
// change to make a formerly non-required argument required, this asserts
|
||||
// failure before allowing the underlying code to receive a non-null value.
|
||||
$result = $this->executeQuery('
|
||||
$result = $this->executeQuery('
|
||||
{
|
||||
fieldWithNonNullableStringInput(input: $foo)
|
||||
}
|
||||
');
|
||||
$expected = [
|
||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||
'errors' => [[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Argument "input" of required type "String!" was provided the ' .
|
||||
'variable "$foo" which was not provided a runtime value.',
|
||||
'locations' => [['line' => 3, 'column' => 48]],
|
||||
'path' => ['fieldWithNonNullableStringInput'],
|
||||
'category' => 'graphql',
|
||||
]]
|
||||
'path' => ['fieldWithNonNullableStringInput'],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
// Describe: Handles lists and nullability
|
||||
|
||||
/**
|
||||
* @see it('allows lists to be null')
|
||||
*/
|
||||
public function testAllowsListsToBeNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]) {
|
||||
list(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$expected = ['data' => ['list' => null]];
|
||||
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
@ -540,12 +609,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsListsToContainValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]) {
|
||||
list(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$expected = ['data' => ['list' => '["A"]']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -555,12 +624,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsListsToContainNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]) {
|
||||
list(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$expected = ['data' => ['list' => '["A",null,"B"]']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -570,22 +639,22 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowNonNullListsToBeNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]!) {
|
||||
nnList(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value null; ' .
|
||||
'Expected non-nullable type [String]! not to be null.',
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -595,12 +664,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNonNullListsToContainValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]!) {
|
||||
nnList(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$expected = ['data' => ['nnList' => '["A"]']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -610,12 +679,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNonNullListsToContainNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String]!) {
|
||||
nnList(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$expected = ['data' => ['nnList' => '["A",null,"B"]']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -625,12 +694,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsListsOfNonNullsToBeNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]) {
|
||||
listNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$expected = ['data' => ['listNN' => null]];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -640,12 +709,12 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsListsOfNonNullsToContainValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]) {
|
||||
listNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$expected = ['data' => ['listNN' => '["A"]']];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -655,22 +724,22 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowListsOfNonNullsToContainNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]) {
|
||||
listNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
||||
'Expected non-nullable type String! not to be null at value[1].',
|
||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -680,22 +749,22 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowNonNullListsOfNonNullsToBeNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]!) {
|
||||
nnListNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$result = $this->executeQuery($doc, ['input' => null]);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value null; ' .
|
||||
'Expected non-nullable type [String!]! not to be null.',
|
||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -705,37 +774,39 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testAllowsNonNullListsOfNonNullsToContainValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]!) {
|
||||
nnListNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||
$expected = ['data' => ['nnListNN' => '["A"]']];
|
||||
$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')
|
||||
*/
|
||||
public function testDoesNotAllowNonNullListsOfNonNullsToContainNull() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input:[String!]!) {
|
||||
nnListNN(input: $input)
|
||||
}
|
||||
';
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
||||
'Expected non-nullable type String! not to be null at value[1].',
|
||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'locations' => [['line' => 2, 'column' => 17]],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -745,23 +816,23 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowInvalidTypesToBeUsedAsValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input: TestType!) {
|
||||
fieldWithObjectInput(input: $input)
|
||||
}
|
||||
';
|
||||
$vars = [ 'input' => [ 'list' => [ 'A', 'B' ] ] ];
|
||||
$result = $this->executeQuery($doc, $vars);
|
||||
$vars = ['input' => ['list' => ['A', 'B']]];
|
||||
$result = $this->executeQuery($doc, $vars);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" expected value of type "TestType!" which cannot ' .
|
||||
'be used as an input type.',
|
||||
'locations' => [['line' => 2, 'column' => 25]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
@ -771,29 +842,28 @@ class VariablesTest extends TestCase
|
||||
*/
|
||||
public function testDoesNotAllowUnknownTypesToBeUsedAsValues() : void
|
||||
{
|
||||
$doc = '
|
||||
$doc = '
|
||||
query q($input: UnknownType!) {
|
||||
fieldWithObjectInput(input: $input)
|
||||
}
|
||||
';
|
||||
$vars = ['input' => 'whoknows'];
|
||||
|
||||
$result = $this->executeQuery($doc, $vars);
|
||||
$result = $this->executeQuery($doc, $vars);
|
||||
$expected = [
|
||||
'errors' => [
|
||||
[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Variable "$input" expected value of type "UnknownType!" which ' .
|
||||
'cannot be used as an input type.',
|
||||
'locations' => [['line' => 2, 'column' => 25]],
|
||||
'category' => 'graphql',
|
||||
]
|
||||
]
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $result->toArray());
|
||||
}
|
||||
|
||||
// Describe: Execute: Uses argument default values
|
||||
/**
|
||||
* @see it('when no argument provided')
|
||||
*/
|
||||
@ -834,84 +904,17 @@ class VariablesTest extends TestCase
|
||||
}');
|
||||
|
||||
$expected = [
|
||||
'data' => ['fieldWithDefaultArgumentValue' => null],
|
||||
'data' => ['fieldWithDefaultArgumentValue' => null],
|
||||
'errors' => [[
|
||||
'message' =>
|
||||
'message' =>
|
||||
'Argument "input" has invalid value WRONG_TYPE.',
|
||||
'locations' => [ [ 'line' => 2, 'column' => 50 ] ],
|
||||
'path' => [ 'fieldWithDefaultArgumentValue' ],
|
||||
'category' => 'graphql',
|
||||
]]
|
||||
'locations' => [['line' => 2, 'column' => 50]],
|
||||
'path' => ['fieldWithDefaultArgumentValue'],
|
||||
'category' => 'graphql',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user