2015-07-15 20:05:46 +03:00
|
|
|
<?php
|
2016-04-09 10:36:53 +03:00
|
|
|
namespace GraphQL\Tests\Executor;
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2015-08-17 17:01:55 +03:00
|
|
|
require_once __DIR__ . '/TestClasses.php';
|
|
|
|
|
2016-04-09 10:36:53 +03:00
|
|
|
use GraphQL\Executor\Executor;
|
2016-05-02 00:42:05 +03:00
|
|
|
use GraphQL\GraphQL;
|
2015-07-15 20:05:46 +03:00
|
|
|
use GraphQL\Language\Parser;
|
|
|
|
use GraphQL\Schema;
|
|
|
|
use GraphQL\Type\Definition\Config;
|
|
|
|
use GraphQL\Type\Definition\InterfaceType;
|
|
|
|
use GraphQL\Type\Definition\ObjectType;
|
2016-05-02 00:42:05 +03:00
|
|
|
use GraphQL\Type\Definition\ResolveInfo;
|
2015-07-15 20:05:46 +03:00
|
|
|
use GraphQL\Type\Definition\Type;
|
|
|
|
use GraphQL\Type\Definition\UnionType;
|
|
|
|
|
|
|
|
class UnionInterfaceTest extends \PHPUnit_Framework_TestCase
|
|
|
|
{
|
|
|
|
public $schema;
|
|
|
|
public $garfield;
|
|
|
|
public $odie;
|
|
|
|
public $liz;
|
|
|
|
public $john;
|
|
|
|
|
|
|
|
public function setUp()
|
|
|
|
{
|
|
|
|
$NamedType = new InterfaceType([
|
|
|
|
'name' => 'Named',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()]
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
|
|
|
$DogType = new ObjectType([
|
|
|
|
'name' => 'Dog',
|
|
|
|
'interfaces' => [$NamedType],
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
2015-08-17 17:01:55 +03:00
|
|
|
'woofs' => ['type' => Type::boolean()]
|
2015-07-15 20:05:46 +03:00
|
|
|
],
|
|
|
|
'isTypeOf' => function ($value) {
|
|
|
|
return $value instanceof Dog;
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
$CatType = new ObjectType([
|
|
|
|
'name' => 'Cat',
|
|
|
|
'interfaces' => [$NamedType],
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'meows' => ['type' => Type::boolean()]
|
|
|
|
],
|
|
|
|
'isTypeOf' => function ($value) {
|
|
|
|
return $value instanceof Cat;
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
$PetType = new UnionType([
|
|
|
|
'name' => 'Pet',
|
|
|
|
'types' => [$DogType, $CatType],
|
|
|
|
'resolveType' => function ($value) use ($DogType, $CatType) {
|
|
|
|
if ($value instanceof Dog) {
|
|
|
|
return $DogType;
|
|
|
|
}
|
|
|
|
if ($value instanceof Cat) {
|
|
|
|
return $CatType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
$PersonType = new ObjectType([
|
|
|
|
'name' => 'Person',
|
|
|
|
'interfaces' => [$NamedType],
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'pets' => ['type' => Type::listOf($PetType)],
|
|
|
|
'friends' => ['type' => Type::listOf($NamedType)]
|
|
|
|
],
|
|
|
|
'isTypeOf' => function ($value) {
|
|
|
|
return $value instanceof Person;
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
$this->schema = new Schema([
|
|
|
|
'query' => $PersonType,
|
|
|
|
'types' => [ $PetType ]
|
|
|
|
]);
|
2015-07-15 20:05:46 +03:00
|
|
|
|
|
|
|
$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]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute: Union and intersection types
|
2016-05-02 00:42:05 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @it can introspect on union and intersection types
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testCanIntrospectOnUnionAndIntersectionTypes()
|
|
|
|
{
|
|
|
|
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
Named: __type(name: "Named") {
|
|
|
|
kind
|
|
|
|
name
|
|
|
|
fields { name }
|
|
|
|
interfaces { name }
|
|
|
|
possibleTypes { name }
|
|
|
|
enumValues { name }
|
|
|
|
inputFields { name }
|
|
|
|
}
|
|
|
|
Pet: __type(name: "Pet") {
|
|
|
|
kind
|
|
|
|
name
|
|
|
|
fields { name }
|
|
|
|
interfaces { name }
|
|
|
|
possibleTypes { name }
|
|
|
|
enumValues { name }
|
|
|
|
inputFields { name }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'Named' => [
|
|
|
|
'kind' => 'INTERFACE',
|
|
|
|
'name' => 'Named',
|
|
|
|
'fields' => [
|
|
|
|
['name' => 'name']
|
|
|
|
],
|
|
|
|
'interfaces' => null,
|
|
|
|
'possibleTypes' => [
|
2016-05-02 00:42:05 +03:00
|
|
|
['name' => 'Person'],
|
2015-07-15 20:05:46 +03:00
|
|
|
['name' => 'Dog'],
|
2016-05-02 00:42:05 +03:00
|
|
|
['name' => 'Cat']
|
2015-07-15 20:05:46 +03:00
|
|
|
],
|
|
|
|
'enumValues' => null,
|
|
|
|
'inputFields' => null
|
|
|
|
],
|
|
|
|
'Pet' => [
|
|
|
|
'kind' => 'UNION',
|
|
|
|
'name' => 'Pet',
|
|
|
|
'fields' => null,
|
|
|
|
'interfaces' => null,
|
|
|
|
'possibleTypes' => [
|
|
|
|
['name' => 'Dog'],
|
|
|
|
['name' => 'Cat']
|
|
|
|
],
|
|
|
|
'enumValues' => null,
|
|
|
|
'inputFields' => null
|
|
|
|
]
|
|
|
|
]
|
|
|
|
];
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray());
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it executes using union types
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testExecutesUsingUnionTypes()
|
|
|
|
{
|
|
|
|
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
pets {
|
|
|
|
__typename
|
|
|
|
name
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'__typename' => 'Person',
|
|
|
|
'name' => 'John',
|
|
|
|
'pets' => [
|
|
|
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it executes union types with inline fragments
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testExecutesUnionTypesWithInlineFragments()
|
|
|
|
{
|
|
|
|
// This is the valid version of the query in the above test.
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
pets {
|
|
|
|
__typename
|
|
|
|
... on Dog {
|
|
|
|
name
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
name
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'__typename' => 'Person',
|
|
|
|
'name' => 'John',
|
|
|
|
'pets' => [
|
|
|
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
]
|
|
|
|
];
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it executes using interface types
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testExecutesUsingInterfaceTypes()
|
|
|
|
{
|
|
|
|
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
friends {
|
|
|
|
__typename
|
|
|
|
name
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'__typename' => 'Person',
|
|
|
|
'name' => 'John',
|
|
|
|
'friends' => [
|
|
|
|
['__typename' => 'Person', 'name' => 'Liz'],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION);
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION, false);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it executes interface types with inline fragments
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testExecutesInterfaceTypesWithInlineFragments()
|
|
|
|
{
|
|
|
|
// This is the valid version of the query in the above test.
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
friends {
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
... on Dog {
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'__typename' => 'Person',
|
|
|
|
'name' => 'John',
|
|
|
|
'friends' => [
|
|
|
|
['__typename' => 'Person', 'name' => 'Liz'],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION);
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION, false);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-05-02 00:42:05 +03:00
|
|
|
/**
|
|
|
|
* @it allows fragment conditions to be abstract types
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testAllowsFragmentConditionsToBeAbstractTypes()
|
|
|
|
{
|
|
|
|
$ast = Parser::parse('
|
|
|
|
{
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
pets { ...PetFields }
|
|
|
|
friends { ...FriendFields }
|
|
|
|
}
|
|
|
|
|
|
|
|
fragment PetFields on Pet {
|
|
|
|
__typename
|
|
|
|
... on Dog {
|
|
|
|
name
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
name
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fragment FriendFields on Named {
|
|
|
|
__typename
|
|
|
|
name
|
|
|
|
... on Dog {
|
2015-08-17 17:01:55 +03:00
|
|
|
woofs
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
... on Cat {
|
|
|
|
meows
|
|
|
|
}
|
|
|
|
}
|
|
|
|
');
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
'data' => [
|
|
|
|
'__typename' => 'Person',
|
|
|
|
'name' => 'John',
|
|
|
|
'pets' => [
|
|
|
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
],
|
|
|
|
'friends' => [
|
|
|
|
['__typename' => 'Person', 'name' => 'Liz'],
|
2015-08-17 17:01:55 +03:00
|
|
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION);
|
2015-08-17 17:01:55 +03:00
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
2017-03-04 23:10:52 +03:00
|
|
|
GraphQL::setIgnoreError(GraphQL::WARNING_ON_IMPLEMENTATION_RESOLUTION, false);
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
2016-05-02 00:42:05 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @it gets execution info in resolver
|
|
|
|
*/
|
|
|
|
public function testGetsExecutionInfoInResolver()
|
|
|
|
{
|
|
|
|
$encounteredContext = null;
|
|
|
|
$encounteredSchema = null;
|
|
|
|
$encounteredRootValue = null;
|
|
|
|
$PersonType2 = null;
|
|
|
|
|
|
|
|
$NamedType2 = new InterfaceType([
|
|
|
|
'name' => 'Named',
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()]
|
|
|
|
],
|
|
|
|
'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',
|
|
|
|
'interfaces' => [$NamedType2],
|
|
|
|
'fields' => [
|
|
|
|
'name' => ['type' => Type::string()],
|
|
|
|
'friends' => ['type' => Type::listOf($NamedType2)],
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$schema2 = new Schema([
|
|
|
|
'query' => $PersonType2
|
|
|
|
]);
|
|
|
|
|
|
|
|
$john2 = new Person('John', [], [$this->liz]);
|
|
|
|
|
|
|
|
$context = ['authToken' => '123abc'];
|
|
|
|
|
|
|
|
$ast = Parser::parse('{ name, friends { name } }');
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
['data' => ['name' => 'John', 'friends' => [['name' => 'Liz']]]],
|
|
|
|
GraphQL::execute($schema2, $ast, $john2, $context)
|
|
|
|
);
|
|
|
|
$this->assertSame($context, $encounteredContext);
|
|
|
|
$this->assertSame($schema2, $encounteredSchema);
|
|
|
|
$this->assertSame($john2, $encounteredRootValue);
|
|
|
|
}
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|