2015-08-17 17:01:55 +03:00
|
|
|
<?php
|
2016-04-09 10:36:53 +03:00
|
|
|
namespace GraphQL\Tests\Executor;
|
2015-08-17 17:01:55 +03:00
|
|
|
|
2016-10-18 18:23:20 +03:00
|
|
|
require_once __DIR__ . '/TestClasses.php';
|
|
|
|
|
2016-04-09 10:36:53 +03:00
|
|
|
use GraphQL\Executor\ExecutionResult;
|
|
|
|
use GraphQL\Executor\Executor;
|
2016-05-02 00:42:05 +03:00
|
|
|
use GraphQL\GraphQL;
|
2015-08-17 17:01:55 +03:00
|
|
|
use GraphQL\Language\Parser;
|
2017-08-14 16:41:08 +03:00
|
|
|
use GraphQL\Type\Schema;
|
2015-08-17 17:01:55 +03:00
|
|
|
use GraphQL\Type\Definition\InterfaceType;
|
|
|
|
use GraphQL\Type\Definition\ObjectType;
|
|
|
|
use GraphQL\Type\Definition\Type;
|
|
|
|
use GraphQL\Type\Definition\UnionType;
|
|
|
|
|
|
|
|
class AbstractTest extends \PHPUnit_Framework_TestCase
|
|
|
|
{
|
|
|
|
// Execute: Handles execution of abstract types
|
2016-05-02 00:42:05 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @it isTypeOf used to resolve runtime type for Interface
|
|
|
|
*/
|
2015-08-17 17:01:55 +03:00
|
|
|
public function testIsTypeOfUsedToResolveRuntimeTypeForInterface()
|
|
|
|
{
|
|
|
|
// isTypeOf used to resolve runtime type for Interface
|
|
|
|
$petType = new InterfaceType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Added to interface type when defined
|
|
|
|
$dogType = new ObjectType([
|
|
|
|
'name' => 'Dog',
|
|
|
|
'interfaces' => [$petType],
|
|
|
|
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'woofs' => ['type' => Type::boolean()]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$catType = new ObjectType([
|
|
|
|
'name' => 'Cat',
|
|
|
|
'interfaces' => [$petType],
|
|
|
|
'isTypeOf' => function ($obj) {
|
|
|
|
return $obj instanceof Cat;
|
|
|
|
},
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'meows' => ['type' => Type::boolean()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
$schema = new Schema([
|
|
|
|
'query' => new ObjectType([
|
2015-08-17 17:01:55 +03:00
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'pets' => [
|
|
|
|
'type' => Type::listOf($petType),
|
|
|
|
'resolve' => function () {
|
|
|
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
2016-05-02 00:42:05 +03:00
|
|
|
]),
|
|
|
|
'types' => [$catType, $dogType]
|
2016-04-25 16:29:17 +03:00
|
|
|
]);
|
2015-08-17 17:01:55 +03:00
|
|
|
|
|
|
|
$query = '{
|
|
|
|
pets {
|
|
|
|
name
|
|
|
|
... on Dog {
|
|
|
|
woofs
|
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}';
|
|
|
|
|
|
|
|
$expected = new ExecutionResult([
|
|
|
|
'pets' => [
|
|
|
|
['name' => 'Odie', 'woofs' => true],
|
|
|
|
['name' => 'Garfield', 'meows' => false]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2017-03-04 23:10:52 +03:00
|
|
|
$result = Executor::execute($schema, Parser::parse($query));
|
|
|
|
$this->assertEquals($expected, $result);
|
2015-08-17 17:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it isTypeOf used to resolve runtime type for Union
|
|
|
|
*/
|
2015-08-17 17:01:55 +03:00
|
|
|
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion()
|
|
|
|
{
|
|
|
|
$dogType = new ObjectType([
|
|
|
|
'name' => 'Dog',
|
|
|
|
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'woofs' => ['type' => Type::boolean()]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$catType = new ObjectType([
|
|
|
|
'name' => 'Cat',
|
|
|
|
'isTypeOf' => function ($obj) {
|
|
|
|
return $obj instanceof Cat;
|
|
|
|
},
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'meows' => ['type' => Type::boolean()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$petType = new UnionType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'types' => [$dogType, $catType]
|
|
|
|
]);
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
$schema = new Schema([
|
|
|
|
'query' => new ObjectType([
|
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'pets' => [
|
|
|
|
'type' => Type::listOf($petType),
|
|
|
|
'resolve' => function() {
|
|
|
|
return [ new Dog('Odie', true), new Cat('Garfield', false) ];
|
|
|
|
}
|
|
|
|
]
|
2015-08-17 17:01:55 +03:00
|
|
|
]
|
2016-04-25 16:29:17 +03:00
|
|
|
])
|
|
|
|
]);
|
2015-08-17 17:01:55 +03:00
|
|
|
|
|
|
|
$query = '{
|
|
|
|
pets {
|
|
|
|
name
|
|
|
|
... on Dog {
|
|
|
|
woofs
|
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}';
|
|
|
|
|
|
|
|
$expected = new ExecutionResult([
|
|
|
|
'pets' => [
|
|
|
|
['name' => 'Odie', 'woofs' => true],
|
|
|
|
['name' => 'Garfield', 'meows' => false]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertEquals($expected, Executor::execute($schema, Parser::parse($query)));
|
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it resolveType on Interface yields useful error
|
|
|
|
*/
|
2015-08-17 17:01:55 +03:00
|
|
|
function testResolveTypeOnInterfaceYieldsUsefulError()
|
|
|
|
{
|
|
|
|
$DogType = null;
|
|
|
|
$CatType = null;
|
|
|
|
$HumanType = null;
|
|
|
|
|
|
|
|
$PetType = new InterfaceType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
|
|
|
if ($obj instanceof Dog) {
|
|
|
|
return $DogType;
|
|
|
|
}
|
|
|
|
if ($obj instanceof Cat) {
|
|
|
|
return $CatType;
|
|
|
|
}
|
|
|
|
if ($obj instanceof Human) {
|
|
|
|
return $HumanType;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$HumanType = new ObjectType([
|
|
|
|
'name' => 'Human',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$DogType = new ObjectType([
|
|
|
|
'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()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
$schema = new Schema([
|
|
|
|
'query' => new ObjectType([
|
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'pets' => [
|
|
|
|
'type' => Type::listOf($PetType),
|
|
|
|
'resolve' => function () {
|
|
|
|
return [
|
|
|
|
new Dog('Odie', true),
|
|
|
|
new Cat('Garfield', false),
|
|
|
|
new Human('Jon')
|
|
|
|
];
|
|
|
|
}
|
|
|
|
]
|
|
|
|
],
|
|
|
|
]),
|
|
|
|
'types' => [$DogType, $CatType]
|
|
|
|
]);
|
2015-08-17 17:01:55 +03:00
|
|
|
|
|
|
|
|
|
|
|
$query = '{
|
|
|
|
pets {
|
|
|
|
name
|
|
|
|
... on Dog {
|
|
|
|
woofs
|
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}';
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'pets' => [
|
|
|
|
['name' => 'Odie', 'woofs' => true],
|
|
|
|
['name' => 'Garfield', 'meows' => false],
|
|
|
|
null
|
|
|
|
]
|
|
|
|
],
|
2016-11-19 00:15:40 +03:00
|
|
|
'errors' => [[
|
2017-08-14 19:52:17 +03:00
|
|
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
2016-11-19 00:15:40 +03:00
|
|
|
'locations' => [['line' => 2, 'column' => 11]],
|
|
|
|
'path' => ['pets', 2]
|
|
|
|
]]
|
2015-08-17 17:01:55 +03:00
|
|
|
];
|
2017-08-14 19:52:17 +03:00
|
|
|
$actual = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
|
2015-08-17 17:01:55 +03:00
|
|
|
|
2017-08-07 22:00:17 +03:00
|
|
|
$this->assertArraySubset($expected, $actual);
|
2015-08-17 17:01:55 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it resolveType on Union yields useful error
|
|
|
|
*/
|
|
|
|
public function testResolveTypeOnUnionYieldsUsefulError()
|
|
|
|
{
|
|
|
|
$HumanType = new ObjectType([
|
|
|
|
'name' => 'Human',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$DogType = new ObjectType([
|
|
|
|
'name' => 'Dog',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'woofs' => ['type' => Type::boolean()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$CatType = new ObjectType([
|
|
|
|
'name' => 'Cat',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'meows' => ['type' => Type::boolean()],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$PetType = new UnionType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
|
|
|
if ($obj instanceof Dog) {
|
|
|
|
return $DogType;
|
|
|
|
}
|
|
|
|
if ($obj instanceof Cat) {
|
|
|
|
return $CatType;
|
|
|
|
}
|
|
|
|
if ($obj instanceof Human) {
|
|
|
|
return $HumanType;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'types' => [$DogType, $CatType]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$schema = new Schema([
|
|
|
|
'query' => new ObjectType([
|
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'pets' => [
|
|
|
|
'type' => Type::listOf($PetType),
|
|
|
|
'resolve' => function () {
|
|
|
|
return [
|
|
|
|
new Dog('Odie', true),
|
|
|
|
new Cat('Garfield', false),
|
|
|
|
new Human('Jon')
|
|
|
|
];
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
])
|
|
|
|
]);
|
|
|
|
|
|
|
|
$query = '{
|
|
|
|
pets {
|
|
|
|
... on Dog {
|
|
|
|
name
|
|
|
|
woofs
|
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
name
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}';
|
|
|
|
|
2017-08-14 19:52:17 +03:00
|
|
|
$result = GraphQL::executeAndReturnResult($schema, $query)->toArray(true);
|
2016-05-02 00:42:05 +03:00
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'pets' => [
|
|
|
|
['name' => 'Odie',
|
|
|
|
'woofs' => true],
|
|
|
|
['name' => 'Garfield',
|
|
|
|
'meows' => false],
|
|
|
|
null
|
|
|
|
]
|
|
|
|
],
|
2016-11-19 00:15:40 +03:00
|
|
|
'errors' => [[
|
2017-08-14 19:52:17 +03:00
|
|
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
2016-11-19 00:15:40 +03:00
|
|
|
'locations' => [['line' => 2, 'column' => 11]],
|
|
|
|
'path' => ['pets', 2]
|
|
|
|
]]
|
2016-05-02 00:42:05 +03:00
|
|
|
];
|
2017-08-07 22:00:17 +03:00
|
|
|
$this->assertArraySubset($expected, $result);
|
2016-05-02 00:42:05 +03:00
|
|
|
}
|
2016-10-18 18:23:20 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @it resolveType allows resolving with type name
|
|
|
|
*/
|
|
|
|
public function testResolveTypeAllowsResolvingWithTypeName()
|
|
|
|
{
|
|
|
|
$PetType = new InterfaceType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'resolveType' => function($obj) {
|
|
|
|
if ($obj instanceof Dog) return 'Dog';
|
|
|
|
if ($obj instanceof Cat) return 'Cat';
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
'fields' => [
|
|
|
|
'name' => [ 'type' => Type::string() ]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$DogType = new ObjectType([
|
|
|
|
'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() ],
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$schema = new Schema([
|
|
|
|
'query' => new ObjectType([
|
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'pets' => [
|
|
|
|
'type' => Type::listOf($PetType),
|
|
|
|
'resolve' => function() {
|
|
|
|
return [
|
|
|
|
new Dog('Odie', true),
|
|
|
|
new Cat('Garfield', false)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
]),
|
|
|
|
'types' => [ $CatType, $DogType ]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$query = '{
|
|
|
|
pets {
|
|
|
|
name
|
|
|
|
... on Dog {
|
|
|
|
woofs
|
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}';
|
|
|
|
|
|
|
|
$result = GraphQL::execute($schema, $query);
|
|
|
|
|
|
|
|
$this->assertEquals([
|
|
|
|
'data' => [
|
|
|
|
'pets' => [
|
|
|
|
['name' => 'Odie', 'woofs' => true],
|
|
|
|
['name' => 'Garfield', 'meows' => false]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
], $result);
|
|
|
|
}
|
2017-08-14 19:52:17 +03:00
|
|
|
|
|
|
|
public function testHintsOnConflictingTypeInstancesInResolveType()
|
|
|
|
{
|
|
|
|
$createTest = function() use (&$iface) {
|
|
|
|
return new ObjectType([
|
|
|
|
'name' => 'Test',
|
|
|
|
'fields' => [
|
|
|
|
'a' => Type::string()
|
|
|
|
],
|
|
|
|
'interfaces' => function() use ($iface) {
|
|
|
|
return [$iface];
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
};
|
|
|
|
|
|
|
|
$iface = new InterfaceType([
|
|
|
|
'name' => 'Node',
|
|
|
|
'fields' => [
|
|
|
|
'a' => Type::string()
|
|
|
|
],
|
|
|
|
'resolveType' => function() use (&$createTest) {
|
|
|
|
return $createTest();
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
$query = new ObjectType([
|
|
|
|
'name' => 'Query',
|
|
|
|
'fields' => [
|
|
|
|
'node' => $iface,
|
|
|
|
'test' => $createTest()
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$schema = new Schema([
|
|
|
|
'query' => $query,
|
|
|
|
]);
|
|
|
|
$schema->assertValid();
|
|
|
|
|
|
|
|
$query = '
|
|
|
|
{
|
|
|
|
node {
|
|
|
|
a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
';
|
|
|
|
|
|
|
|
$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.',
|
|
|
|
$result->errors[0]->getMessage()
|
|
|
|
);
|
|
|
|
}
|
2015-08-17 17:01:55 +03:00
|
|
|
}
|