Merge pull request #344 from simPod/fix-cs-test-type

Fix CS in tests/Type
This commit is contained in:
Vladimir Razuvaev 2018-09-02 21:26:11 +07:00 committed by GitHub
commit 4e43a2cbcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 2565 additions and 2473 deletions

View File

@ -1,11 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
require_once __DIR__ . '/TestClasses.php';
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Tests\Type\TestClasses\MyCustomType;
use GraphQL\Tests\Type\TestClasses\OtherCustom;
use GraphQL\Type\Definition\CustomScalarType; use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\InterfaceType;
@ -14,74 +16,53 @@ use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Schema;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function count;
use function get_class;
use function json_encode;
use function sprintf;
class DefinitionTest extends TestCase class DefinitionTest extends TestCase
{ {
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogImage; public $blogImage;
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogArticle; public $blogArticle;
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogAuthor; public $blogAuthor;
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogMutation; public $blogMutation;
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogQuery; public $blogQuery;
/** /** @var ObjectType */
* @var ObjectType
*/
public $blogSubscription; public $blogSubscription;
/** /** @var ObjectType */
* @var ObjectType
*/
public $objectType; public $objectType;
/** /** @var ObjectType */
* @var ObjectType
*/
public $objectWithIsTypeOf; public $objectWithIsTypeOf;
/** /** @var InterfaceType */
* @var InterfaceType
*/
public $interfaceType; public $interfaceType;
/** /** @var UnionType */
* @var UnionType
*/
public $unionType; public $unionType;
/** /** @var EnumType */
* @var EnumType
*/
public $enumType; public $enumType;
/** /** @var InputObjectType */
* @var InputObjectType
*/
public $inputObjectType; public $inputObjectType;
/** /** @var CustomScalarType */
* @var CustomScalarType
*/
public $scalarType; public $scalarType;
public function setUp() public function setUp()
@ -99,9 +80,12 @@ class DefinitionTest extends TestCase
$this->scalarType = new CustomScalarType([ $this->scalarType = new CustomScalarType([
'name' => 'Scalar', 'name' => 'Scalar',
'serialize' => function () {}, 'serialize' => function () {
'parseValue' => function () {}, },
'parseLiteral' => function () {}, 'parseValue' => function () {
},
'parseLiteral' => function () {
},
]); ]);
$this->blogImage = new ObjectType([ $this->blogImage = new ObjectType([
@ -109,20 +93,23 @@ class DefinitionTest extends TestCase
'fields' => [ 'fields' => [
'url' => ['type' => Type::string()], 'url' => ['type' => Type::string()],
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
] ],
]); ]);
$this->blogAuthor = new ObjectType([ $this->blogAuthor = new ObjectType([
'name' => 'Author', 'name' => 'Author',
'fields' => function() { 'fields' => function () {
return [ return [
'id' => ['type' => Type::string()], 'id' => ['type' => Type::string()],
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'pic' => [ 'type' => $this->blogImage, 'args' => [ 'pic' => [
'type' => $this->blogImage,
'args' => [
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
]], ],
],
'recentArticle' => $this->blogArticle, 'recentArticle' => $this->blogArticle,
]; ];
}, },
@ -135,35 +122,38 @@ class DefinitionTest extends TestCase
'isPublished' => ['type' => Type::boolean()], 'isPublished' => ['type' => Type::boolean()],
'author' => ['type' => $this->blogAuthor], 'author' => ['type' => $this->blogAuthor],
'title' => ['type' => Type::string()], 'title' => ['type' => Type::string()],
'body' => ['type' => Type::string()] 'body' => ['type' => Type::string()],
] ],
]); ]);
$this->blogQuery = new ObjectType([ $this->blogQuery = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'article' => ['type' => $this->blogArticle, 'args' => [ 'article' => [
'id' => ['type' => Type::string()] 'type' => $this->blogArticle,
]], 'args' => [
'feed' => ['type' => new ListOfType($this->blogArticle)] 'id' => ['type' => Type::string()],
] ],
],
'feed' => ['type' => new ListOfType($this->blogArticle)],
],
]); ]);
$this->blogMutation = new ObjectType([ $this->blogMutation = new ObjectType([
'name' => 'Mutation', 'name' => 'Mutation',
'fields' => [ 'fields' => [
'writeArticle' => ['type' => $this->blogArticle] 'writeArticle' => ['type' => $this->blogArticle],
] ],
]); ]);
$this->blogSubscription = new ObjectType([ $this->blogSubscription = new ObjectType([
'name' => 'Subscription', 'name' => 'Subscription',
'fields' => [ 'fields' => [
'articleSubscribe' => [ 'articleSubscribe' => [
'args' => [ 'id' => [ 'type' => Type::string() ]], 'args' => ['id' => ['type' => Type::string()]],
'type' => $this->blogArticle 'type' => $this->blogArticle,
] ],
] ],
]); ]);
} }
@ -175,7 +165,7 @@ class DefinitionTest extends TestCase
public function testDefinesAQueryOnlySchema() : void public function testDefinesAQueryOnlySchema() : void
{ {
$blogSchema = new Schema([ $blogSchema = new Schema([
'query' => $this->blogQuery 'query' => $this->blogQuery,
]); ]);
$this->assertSame($blogSchema->getQueryType(), $this->blogQuery); $this->assertSame($blogSchema->getQueryType(), $this->blogQuery);
@ -220,7 +210,7 @@ class DefinitionTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->blogQuery, 'query' => $this->blogQuery,
'mutation' => $this->blogMutation 'mutation' => $this->blogMutation,
]); ]);
$this->assertSame($this->blogMutation, $schema->getMutationType()); $this->assertSame($this->blogMutation, $schema->getMutationType());
@ -239,7 +229,7 @@ class DefinitionTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->blogQuery, 'query' => $this->blogQuery,
'subscription' => $this->blogSubscription 'subscription' => $this->blogSubscription,
]); ]);
$this->assertEquals($this->blogSubscription, $schema->getSubscriptionType()); $this->assertEquals($this->blogSubscription, $schema->getSubscriptionType());
@ -258,19 +248,22 @@ class DefinitionTest extends TestCase
$enumTypeWithDeprecatedValue = new EnumType([ $enumTypeWithDeprecatedValue = new EnumType([
'name' => 'EnumWithDeprecatedValue', 'name' => 'EnumWithDeprecatedValue',
'values' => [ 'values' => [
'foo' => ['deprecationReason' => 'Just because'] 'foo' => ['deprecationReason' => 'Just because'],
] ],
]); ]);
$value = $enumTypeWithDeprecatedValue->getValues()[0]; $value = $enumTypeWithDeprecatedValue->getValues()[0];
$this->assertArraySubset([ $this->assertArraySubset(
[
'name' => 'foo', 'name' => 'foo',
'description' => null, 'description' => null,
'deprecationReason' => 'Just because', 'deprecationReason' => 'Just because',
'value' => 'foo', 'value' => 'foo',
'astNode' => null 'astNode' => null,
], (array) $value); ],
(array) $value
);
$this->assertEquals(true, $value->isDeprecated()); $this->assertEquals(true, $value->isDeprecated());
} }
@ -285,7 +278,7 @@ class DefinitionTest extends TestCase
'values' => [ 'values' => [
'NULL' => ['value' => null], 'NULL' => ['value' => null],
'UNDEFINED' => ['value' => null], 'UNDEFINED' => ['value' => null],
] ],
]); ]);
$expected = [ $expected = [
@ -308,8 +301,8 @@ class DefinitionTest extends TestCase
$actual = $EnumTypeWithNullishValue->getValues(); $actual = $EnumTypeWithNullishValue->getValues();
$this->assertEquals(count($expected), count($actual)); $this->assertEquals(count($expected), count($actual));
$this->assertArraySubset($expected[0], (array)$actual[0]); $this->assertArraySubset($expected[0], (array) $actual[0]);
$this->assertArraySubset($expected[1], (array)$actual[1]); $this->assertArraySubset($expected[1], (array) $actual[1]);
} }
/** /**
@ -322,9 +315,9 @@ class DefinitionTest extends TestCase
'fields' => [ 'fields' => [
'bar' => [ 'bar' => [
'type' => Type::string(), 'type' => Type::string(),
'deprecationReason' => 'A terrible reason' 'deprecationReason' => 'A terrible reason',
] ],
] ],
]); ]);
$field = $TypeWithDeprecatedField->getField('bar'); $field = $TypeWithDeprecatedField->getField('bar');
@ -343,25 +336,25 @@ class DefinitionTest extends TestCase
{ {
$nestedInputObject = new InputObjectType([ $nestedInputObject = new InputObjectType([
'name' => 'NestedInputObject', 'name' => 'NestedInputObject',
'fields' => ['value' => ['type' => Type::string()]] 'fields' => ['value' => ['type' => Type::string()]],
]); ]);
$someInputObject = new InputObjectType([ $someInputObject = new InputObjectType([
'name' => 'SomeInputObject', 'name' => 'SomeInputObject',
'fields' => ['nested' => ['type' => $nestedInputObject]] 'fields' => ['nested' => ['type' => $nestedInputObject]],
]); ]);
$someMutation = new ObjectType([ $someMutation = new ObjectType([
'name' => 'SomeMutation', 'name' => 'SomeMutation',
'fields' => [ 'fields' => [
'mutateSomething' => [ 'mutateSomething' => [
'type' => $this->blogArticle, 'type' => $this->blogArticle,
'args' => ['input' => ['type' => $someInputObject]] 'args' => ['input' => ['type' => $someInputObject]],
] ],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => $this->blogQuery, 'query' => $this->blogQuery,
'mutation' => $someMutation 'mutation' => $someMutation,
]); ]);
$this->assertSame($nestedInputObject, $schema->getType('NestedInputObject')); $this->assertSame($nestedInputObject, $schema->getType('NestedInputObject'));
} }
@ -374,14 +367,14 @@ class DefinitionTest extends TestCase
$someInterface = new InterfaceType([ $someInterface = new InterfaceType([
'name' => 'SomeInterface', 'name' => 'SomeInterface',
'fields' => [ 'fields' => [
'f' => ['type' => Type::int()] 'f' => ['type' => Type::int()],
] ],
]); ]);
$someSubtype = new ObjectType([ $someSubtype = new ObjectType([
'name' => 'SomeSubtype', 'name' => 'SomeSubtype',
'fields' => [ 'fields' => [
'f' => ['type' => Type::int()] 'f' => ['type' => Type::int()],
], ],
'interfaces' => [$someInterface], 'interfaces' => [$someInterface],
]); ]);
@ -390,10 +383,10 @@ class DefinitionTest extends TestCase
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'iface' => ['type' => $someInterface] 'iface' => ['type' => $someInterface],
] ],
]), ]),
'types' => [$someSubtype] 'types' => [$someSubtype],
]); ]);
$this->assertSame($someSubtype, $schema->getType('SomeSubtype')); $this->assertSame($someSubtype, $schema->getType('SomeSubtype'));
} }
@ -408,26 +401,28 @@ class DefinitionTest extends TestCase
$someSubtype = new ObjectType([ $someSubtype = new ObjectType([
'name' => 'SomeSubtype', 'name' => 'SomeSubtype',
'fields' => [ 'fields' => [
'f' => ['type' => Type::int()] 'f' => ['type' => Type::int()],
], ],
'interfaces' => function() use (&$someInterface) { return [$someInterface]; }, 'interfaces' => function () use (&$someInterface) {
return [$someInterface];
},
]); ]);
$someInterface = new InterfaceType([ $someInterface = new InterfaceType([
'name' => 'SomeInterface', 'name' => 'SomeInterface',
'fields' => [ 'fields' => [
'f' => ['type' => Type::int()] 'f' => ['type' => Type::int()],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'iface' => ['type' => $someInterface] 'iface' => ['type' => $someInterface],
] ],
]), ]),
'types' => [$someSubtype] 'types' => [$someSubtype],
]); ]);
$this->assertSame($someSubtype, $schema->getType('SomeSubtype')); $this->assertSame($someSubtype, $schema->getType('SomeSubtype'));
@ -483,11 +478,15 @@ class DefinitionTest extends TestCase
[$this->interfaceType, false], [$this->interfaceType, false],
[$this->unionType, false], [$this->unionType, false],
[$this->enumType, true], [$this->enumType, true],
[$this->inputObjectType, true] [$this->inputObjectType, true],
]; ];
foreach ($expected as $index => $entry) { foreach ($expected as $index => $entry) {
$this->assertSame($entry[1], Type::isInputType($entry[0]), "Type {$entry[0]} was detected incorrectly"); $this->assertSame(
$entry[1],
Type::isInputType($entry[0]),
sprintf('Type %s was detected incorrectly', $entry[0])
);
} }
} }
@ -502,11 +501,15 @@ class DefinitionTest extends TestCase
[$this->interfaceType, true], [$this->interfaceType, true],
[$this->unionType, true], [$this->unionType, true],
[$this->enumType, true], [$this->enumType, true],
[$this->inputObjectType, false] [$this->inputObjectType, false],
]; ];
foreach ($expected as $index => $entry) { foreach ($expected as $index => $entry) {
$this->assertSame($entry[1], Type::isOutputType($entry[0]), "Type {$entry[0]} was detected incorrectly"); $this->assertSame(
$entry[1],
Type::isOutputType($entry[0]),
sprintf('Type %s was detected incorrectly', $entry[0])
);
} }
} }
@ -529,7 +532,9 @@ class DefinitionTest extends TestCase
{ {
$union = new UnionType([ $union = new UnionType([
'name' => 'ThunkUnion', 'name' => 'ThunkUnion',
'types' => function() {return [$this->objectType]; } 'types' => function () {
return [$this->objectType];
},
]); ]);
$types = $union->getTypes(); $types = $union->getTypes();
@ -543,8 +548,8 @@ class DefinitionTest extends TestCase
$node = new InterfaceType([ $node = new InterfaceType([
'name' => 'Node', 'name' => 'Node',
'fields' => [ 'fields' => [
'id' => ['type' => Type::nonNull(Type::id())] 'id' => ['type' => Type::nonNull(Type::id())],
] ],
]); ]);
$blog = null; $blog = null;
@ -552,41 +557,41 @@ class DefinitionTest extends TestCase
$user = new ObjectType([ $user = new ObjectType([
'name' => 'User', 'name' => 'User',
'fields' => function() use (&$blog, &$called) { 'fields' => function () use (&$blog, &$called) {
$this->assertNotNull($blog, 'Blog type is expected to be defined at this point, but it is null'); $this->assertNotNull($blog, 'Blog type is expected to be defined at this point, but it is null');
$called = true; $called = true;
return [ return [
'id' => ['type' => Type::nonNull(Type::id())], 'id' => ['type' => Type::nonNull(Type::id())],
'blogs' => ['type' => Type::nonNull(Type::listOf(Type::nonNull($blog)))] 'blogs' => ['type' => Type::nonNull(Type::listOf(Type::nonNull($blog)))],
]; ];
}, },
'interfaces' => function() use ($node) { 'interfaces' => function () use ($node) {
return [$node]; return [$node];
} },
]); ]);
$blog = new ObjectType([ $blog = new ObjectType([
'name' => 'Blog', 'name' => 'Blog',
'fields' => function() use ($user) { 'fields' => function () use ($user) {
return [ return [
'id' => ['type' => Type::nonNull(Type::id())], 'id' => ['type' => Type::nonNull(Type::id())],
'owner' => ['type' => Type::nonNull($user)] 'owner' => ['type' => Type::nonNull($user)],
]; ];
}, },
'interfaces' => function() use ($node) { 'interfaces' => function () use ($node) {
return [$node]; return [$node];
} },
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'node' => ['type' => $node] 'node' => ['type' => $node],
] ],
]), ]),
'types' => [$user, $blog] 'types' => [$user, $blog],
]); ]);
$this->assertTrue($called); $this->assertTrue($called);
@ -607,27 +612,29 @@ class DefinitionTest extends TestCase
$called = false; $called = false;
$inputObject = new InputObjectType([ $inputObject = new InputObjectType([
'name' => 'InputObject', 'name' => 'InputObject',
'fields' => function() use (&$inputObject, &$called) { 'fields' => function () use (&$inputObject, &$called) {
$called = true; $called = true;
return [ return [
'value' => ['type' => Type::string()], 'value' => ['type' => Type::string()],
'nested' => ['type' => $inputObject ] 'nested' => ['type' => $inputObject],
]; ];
} },
]); ]);
$someMutation = new ObjectType([ $someMutation = new ObjectType([
'name' => 'SomeMutation', 'name' => 'SomeMutation',
'fields' => [ 'fields' => [
'mutateSomething' => [ 'mutateSomething' => [
'type' => $this->blogArticle, 'type' => $this->blogArticle,
'args' => ['input' => ['type' => $inputObject]] 'args' => ['input' => ['type' => $inputObject]],
] ],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => $this->blogQuery, 'query' => $this->blogQuery,
'mutation' => $someMutation 'mutation' => $someMutation,
]); ]);
$this->assertSame($inputObject, $schema->getType('InputObject')); $this->assertSame($inputObject, $schema->getType('InputObject'));
@ -642,25 +649,24 @@ class DefinitionTest extends TestCase
$called = false; $called = false;
$interface = new InterfaceType([ $interface = new InterfaceType([
'name' => 'SomeInterface', 'name' => 'SomeInterface',
'fields' => function() use (&$interface, &$called) { 'fields' => function () use (&$interface, &$called) {
$called = true; $called = true;
return [ return [
'value' => ['type' => Type::string()], 'value' => ['type' => Type::string()],
'nested' => ['type' => $interface ] 'nested' => ['type' => $interface],
]; ];
} },
]); ]);
$query = new ObjectType([ $query = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => [
'test' => ['type' => $interface] 'test' => ['type' => $interface],
] ],
]); ]);
$schema = new Schema([ $schema = new Schema(['query' => $query]);
'query' => $query
]);
$this->assertSame($interface, $schema->getType('SomeInterface')); $this->assertSame($interface, $schema->getType('SomeInterface'));
$this->assertTrue($called); $this->assertTrue($called);
@ -673,30 +679,26 @@ class DefinitionTest extends TestCase
{ {
$interface = new InterfaceType([ $interface = new InterfaceType([
'name' => 'SomeInterface', 'name' => 'SomeInterface',
'fields' => function() use (&$interface) { 'fields' => function () use (&$interface) {
return [ return [
'value' => Type::string(), 'value' => Type::string(),
'nested' => $interface, 'nested' => $interface,
'withArg' => [ 'withArg' => [
'type' => Type::string(), 'type' => Type::string(),
'args' => [ 'args' => [
'arg1' => Type::int() 'arg1' => Type::int(),
] ],
] ],
]; ];
} },
]); ]);
$query = new ObjectType([ $query = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => [ 'fields' => ['test' => $interface],
'test' => $interface
]
]); ]);
$schema = new Schema([ $schema = new Schema(['query' => $query]);
'query' => $query
]);
$valueField = $schema->getType('SomeInterface')->getField('value'); $valueField = $schema->getType('SomeInterface')->getField('value');
$nestedField = $schema->getType('SomeInterface')->getField('nested'); $nestedField = $schema->getType('SomeInterface')->getField('nested');
@ -728,14 +730,17 @@ class DefinitionTest extends TestCase
{ {
$idType = new CustomScalarType([ $idType = new CustomScalarType([
'name' => 'ID', 'name' => 'ID',
'serialize' => function() {}, 'serialize' => function () {
'parseValue' => function() {}, },
'parseLiteral' => function() {} 'parseValue' => function () {
},
'parseLiteral' => function () {
},
]); ]);
$schema = new Schema([ $schema = new Schema([
'query' => new ObjectType(['name' => 'Query', 'fields' => []]), 'query' => new ObjectType(['name' => 'Query', 'fields' => []]),
'types' => [$idType] 'types' => [$idType],
]); ]);
$this->assertSame($idType, $schema->getType('ID')); $this->assertSame($idType, $schema->getType('ID'));
@ -767,9 +772,7 @@ class DefinitionTest extends TestCase
{ {
$objType = new ObjectType([ $objType = new ObjectType([
'name' => 'SomeObject', 'name' => 'SomeObject',
'fields' => [ 'fields' => ['f' => null],
'f' => null,
],
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
$this->expectExceptionMessage( $this->expectExceptionMessage(
@ -929,6 +932,17 @@ class DefinitionTest extends TestCase
// Type System: Object fields must have valid resolve values // Type System: Object fields must have valid resolve values
/**
* @see it('accepts a lambda as an Object field resolver')
*/
public function testAcceptsALambdaAsAnObjectFieldResolver() : void
{
$this->expectNotToPerformAssertions();
// should not throw:
$this->schemaWithObjectWithFieldResolver(function () {
});
}
private function schemaWithObjectWithFieldResolver($resolveValue) private function schemaWithObjectWithFieldResolver($resolveValue)
{ {
$BadResolverType = new ObjectType([ $BadResolverType = new ObjectType([
@ -950,17 +964,8 @@ class DefinitionTest extends TestCase
]), ]),
]); ]);
$schema->assertValid(); $schema->assertValid();
return $schema;
}
/** return $schema;
* @see it('accepts a lambda as an Object field resolver')
*/
public function testAcceptsALambdaAsAnObjectFieldResolver() : void
{
$this->expectNotToPerformAssertions();
// should not throw:
$this->schemaWithObjectWithFieldResolver(function () {});
} }
/** /**
@ -987,21 +992,8 @@ class DefinitionTest extends TestCase
$this->schemaWithObjectWithFieldResolver(0); $this->schemaWithObjectWithFieldResolver(0);
} }
// Type System: Interface types must be resolvable // Type System: Interface types must be resolvable
private function schemaWithFieldType($type)
{
$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => ['field' => ['type' => $type]],
]),
'types' => [$type],
]);
$schema->assertValid();
return $schema;
}
/** /**
* @see it('accepts an Interface type defining resolveType') * @see it('accepts an Interface type defining resolveType')
*/ */
@ -1023,6 +1015,20 @@ class DefinitionTest extends TestCase
); );
} }
private function schemaWithFieldType($type)
{
$schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => ['field' => ['type' => $type]],
]),
'types' => [$type],
]);
$schema->assertValid();
return $schema;
}
/** /**
* @see it('accepts an Interface with implementing type defining isTypeOf') * @see it('accepts an Interface with implementing type defining isTypeOf')
*/ */
@ -1085,14 +1091,6 @@ class DefinitionTest extends TestCase
// Type System: Union types must be resolvable // Type System: Union types must be resolvable
private function ObjectWithIsTypeOf()
{
return new ObjectType([
'name' => 'ObjectWithIsTypeOf',
'fields' => ['f' => ['type' => Type::string()]],
]);
}
/** /**
* @see it('accepts a Union type defining resolveType') * @see it('accepts a Union type defining resolveType')
*/ */
@ -1156,8 +1154,6 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Scalar types must be serializable
/** /**
* @see it('accepts a Scalar type defining serialize') * @see it('accepts a Scalar type defining serialize')
*/ */
@ -1175,6 +1171,8 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Scalar types must be serializable
/** /**
* @see it('rejects a Scalar type not defining serialize') * @see it('rejects a Scalar type not defining serialize')
*/ */
@ -1187,9 +1185,7 @@ class DefinitionTest extends TestCase
'functions are also provided.' 'functions are also provided.'
); );
$this->schemaWithFieldType( $this->schemaWithFieldType(
new CustomScalarType([ new CustomScalarType(['name' => 'SomeScalar'])
'name' => 'SomeScalar',
])
); );
} }
@ -1292,8 +1288,6 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Object types must be assertable
/** /**
* @see it('accepts an Object type with an isTypeOf function') * @see it('accepts an Object type with an isTypeOf function')
*/ */
@ -1309,6 +1303,8 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Object types must be assertable
/** /**
* @see it('rejects an Object type with an incorrect type for isTypeOf') * @see it('rejects an Object type with an incorrect type for isTypeOf')
*/ */
@ -1327,8 +1323,6 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Union types must be array
/** /**
* @see it('accepts a Union type with array types') * @see it('accepts a Union type with array types')
*/ */
@ -1344,6 +1338,8 @@ class DefinitionTest extends TestCase
); );
} }
// Type System: Union types must be array
/** /**
* @see it('accepts a Union type with function returning an array of types') * @see it('accepts a Union type with function returning an array of types')
*/ */
@ -1370,9 +1366,7 @@ class DefinitionTest extends TestCase
'Must provide Array of types or a callable which returns such an array for Union SomeUnion' 'Must provide Array of types or a callable which returns such an array for Union SomeUnion'
); );
$this->schemaWithFieldType( $this->schemaWithFieldType(
new UnionType([ new UnionType(['name' => 'SomeUnion'])
'name' => 'SomeUnion',
])
); );
} }
@ -1388,13 +1382,11 @@ class DefinitionTest extends TestCase
$this->schemaWithFieldType( $this->schemaWithFieldType(
new UnionType([ new UnionType([
'name' => 'SomeUnion', 'name' => 'SomeUnion',
'types' => (object)[ 'test' => $this->objectType, ], 'types' => (object) ['test' => $this->objectType],
]) ])
); );
} }
// Type System: Input Objects must have fields
/** /**
* @see it('accepts an Input Object type with fields') * @see it('accepts an Input Object type with fields')
*/ */
@ -1410,6 +1402,8 @@ class DefinitionTest extends TestCase
$this->assertSame(Type::string(), $inputObjType->getField('f')->getType()); $this->assertSame(Type::string(), $inputObjType->getField('f')->getType());
} }
// Type System: Input Objects must have fields
/** /**
* @see it('accepts an Input Object type with a field function') * @see it('accepts an Input Object type with a field function')
*/ */
@ -1438,7 +1432,7 @@ class DefinitionTest extends TestCase
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
$this->expectExceptionMessage( $this->expectExceptionMessage(
'SomeInputObject fields must be an associative array with field names as keys or a callable '. 'SomeInputObject fields must be an associative array with field names as keys or a callable ' .
'which returns such an array.' 'which returns such an array.'
); );
$inputObjType->assertValid(); $inputObjType->assertValid();
@ -1463,8 +1457,6 @@ class DefinitionTest extends TestCase
$inputObjType->assertValid(); $inputObjType->assertValid();
} }
// Type System: Input Object fields must not have resolvers
/** /**
* @see it('rejects an Input Object type with resolvers') * @see it('rejects an Input Object type with resolvers')
*/ */
@ -1489,6 +1481,8 @@ class DefinitionTest extends TestCase
$inputObjType->assertValid(); $inputObjType->assertValid();
} }
// Type System: Input Object fields must not have resolvers
/** /**
* @see it('rejects an Input Object type with resolver constant') * @see it('rejects an Input Object type with resolver constant')
*/ */
@ -1511,8 +1505,6 @@ class DefinitionTest extends TestCase
$inputObjType->assertValid(); $inputObjType->assertValid();
} }
// Type System: Enum types must be well defined
/** /**
* @see it('accepts a well defined Enum type with empty value definition') * @see it('accepts a well defined Enum type with empty value definition')
*/ */
@ -1529,6 +1521,8 @@ class DefinitionTest extends TestCase
$this->assertEquals('BAR', $enumType->getValue('BAR')->value); $this->assertEquals('BAR', $enumType->getValue('BAR')->value);
} }
// Type System: Enum types must be well defined
/** /**
* @see it('accepts a well defined Enum type with internal value definition') * @see it('accepts a well defined Enum type with internal value definition')
*/ */
@ -1569,9 +1563,7 @@ class DefinitionTest extends TestCase
$enumType = new EnumType([ $enumType = new EnumType([
'name' => 'SomeEnum', 'name' => 'SomeEnum',
'values' => [ 'values' => [
'FOO' => [ 'FOO' => ['isDeprecated' => true],
'isDeprecated' => true,
],
], ],
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
@ -1582,7 +1574,6 @@ class DefinitionTest extends TestCase
$enumType->assertValid(); $enumType->assertValid();
} }
/** /**
* Type System: List must accept only types * Type System: List must accept only types
*/ */
@ -1606,21 +1597,20 @@ class DefinitionTest extends TestCase
try { try {
Type::listOf($type); Type::listOf($type);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->fail("List is expected to accept type: " . get_class($type) . ", but got error: ". $e->getMessage()); $this->fail('List is expected to accept type: ' . get_class($type) . ', but got error: ' . $e->getMessage());
} }
} }
foreach ($badTypes as $badType) { foreach ($badTypes as $badType) {
$typeStr = Utils::printSafe($badType); $typeStr = Utils::printSafe($badType);
try { try {
Type::listOf($badType); Type::listOf($badType);
$this->fail("List should not accept $typeStr"); $this->fail(sprintf('List should not accept %s', $typeStr));
} catch (InvariantViolation $e) { } catch (InvariantViolation $e) {
$this->assertEquals("Expected $typeStr to be a GraphQL type.", $e->getMessage()); $this->assertEquals(sprintf('Expected %s to be a GraphQL type.', $typeStr), $e->getMessage());
} }
} }
} }
/** /**
* Type System: NonNull must only accept non-nullable types * Type System: NonNull must only accept non-nullable types
*/ */
@ -1648,22 +1638,20 @@ class DefinitionTest extends TestCase
try { try {
Type::nonNull($type); Type::nonNull($type);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->fail("NonNull is expected to accept type: " . get_class($type) . ", but got error: ". $e->getMessage()); $this->fail('NonNull is expected to accept type: ' . get_class($type) . ', but got error: ' . $e->getMessage());
} }
} }
foreach ($notNullableTypes as $badType) { foreach ($notNullableTypes as $badType) {
$typeStr = Utils::printSafe($badType); $typeStr = Utils::printSafe($badType);
try { try {
Type::nonNull($badType); Type::nonNull($badType);
$this->fail("Nulls should not accept $typeStr"); $this->fail(sprintf('Nulls should not accept %s', $typeStr));
} catch (InvariantViolation $e) { } catch (InvariantViolation $e) {
$this->assertEquals("Expected $typeStr to be a GraphQL nullable type.", $e->getMessage()); $this->assertEquals(sprintf('Expected %s to be a GraphQL nullable type.', $typeStr), $e->getMessage());
} }
} }
} }
// Type System: A Schema must contain uniquely named types
/** /**
* @see it('rejects a Schema which redefines a built-in type') * @see it('rejects a Schema which redefines a built-in type')
*/ */
@ -1685,13 +1673,15 @@ class DefinitionTest extends TestCase
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
$this->expectExceptionMessage( $this->expectExceptionMessage(
'Schema must contain unique named types but contains multiple types named "String" '. 'Schema must contain unique named types but contains multiple types named "String" ' .
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).'
); );
$schema = new Schema(['query' => $QueryType]); $schema = new Schema(['query' => $QueryType]);
$schema->assertValid(); $schema->assertValid();
} }
// Type System: A Schema must contain uniquely named types
/** /**
* @see it('rejects a Schema which defines an object type twice') * @see it('rejects a Schema which defines an object type twice')
*/ */
@ -1719,7 +1709,7 @@ class DefinitionTest extends TestCase
'Schema must contain unique named types but contains multiple types named "SameName" ' . 'Schema must contain unique named types but contains multiple types named "SameName" ' .
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).'
); );
$schema = new Schema([ 'query' => $QueryType ]); $schema = new Schema(['query' => $QueryType]);
$schema->assertValid(); $schema->assertValid();
} }
@ -1763,4 +1753,12 @@ class DefinitionTest extends TestCase
]); ]);
$schema->assertValid(); $schema->assertValid();
} }
public function objectWithIsTypeOf() : ObjectType
{
return new ObjectType([
'name' => 'ObjectWithIsTypeOf',
'fields' => ['f' => ['type' => Type::string()]],
]);
}
} }

View File

@ -1,29 +1,33 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
use ArrayObject;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Language\SourceLocation; use GraphQL\Language\SourceLocation;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\EnumType; use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Introspection; use GraphQL\Type\Introspection;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function count;
use function is_array;
class EnumTypeTest extends TestCase class EnumTypeTest extends TestCase
{ {
/** /** @var Schema */
* @var Schema
*/
private $schema; private $schema;
/** /** @var EnumType */
* @var EnumType
*/
private $ComplexEnum; private $ComplexEnum;
/** @var mixed[] */
private $Complex1; private $Complex1;
/** @var ArrayObject */
private $Complex2; private $Complex2;
public function setUp() public function setUp()
@ -34,25 +38,30 @@ class EnumTypeTest extends TestCase
'RED' => ['value' => 0], 'RED' => ['value' => 0],
'GREEN' => ['value' => 1], 'GREEN' => ['value' => 1],
'BLUE' => ['value' => 2], 'BLUE' => ['value' => 2],
] ],
]); ]);
$simpleEnum = new EnumType([ $simpleEnum = new EnumType([
'name' => 'SimpleEnum', 'name' => 'SimpleEnum',
'values' => [ 'values' => [
'ONE', 'TWO', 'THREE' 'ONE',
] 'TWO',
'THREE',
],
]); ]);
$Complex1 = ['someRandomFunction' => function() {}]; $Complex1 = [
'someRandomFunction' => function () {
},
];
$Complex2 = new \ArrayObject(['someRandomValue' => 123]); $Complex2 = new \ArrayObject(['someRandomValue' => 123]);
$ComplexEnum = new EnumType([ $ComplexEnum = new EnumType([
'name' => 'Complex', 'name' => 'Complex',
'values' => [ 'values' => [
'ONE' => ['value' => $Complex1], 'ONE' => ['value' => $Complex1],
'TWO' => ['value' => $Complex2] 'TWO' => ['value' => $Complex2],
] ],
]); ]);
$QueryType = new ObjectType([ $QueryType = new ObjectType([
@ -75,22 +84,22 @@ class EnumTypeTest extends TestCase
if (isset($args['fromEnum'])) { if (isset($args['fromEnum'])) {
return $args['fromEnum']; return $args['fromEnum'];
} }
} },
], ],
'simpleEnum' => [ 'simpleEnum' => [
'type' => $simpleEnum, 'type' => $simpleEnum,
'args' => [ 'args' => [
'fromName' => ['type' => Type::string()], 'fromName' => ['type' => Type::string()],
'fromValue' => ['type' => Type::string()] 'fromValue' => ['type' => Type::string()],
], ],
'resolve' => function($value, $args) { 'resolve' => function ($value, $args) {
if (isset($args['fromName'])) { if (isset($args['fromName'])) {
return $args['fromName']; return $args['fromName'];
} }
if (isset($args['fromValue'])) { if (isset($args['fromValue'])) {
return $args['fromValue']; return $args['fromValue'];
} }
} },
], ],
'colorInt' => [ 'colorInt' => [
'type' => Type::int(), 'type' => Type::int(),
@ -105,7 +114,7 @@ class EnumTypeTest extends TestCase
if (isset($args['fromEnum'])) { if (isset($args['fromEnum'])) {
return $args['fromEnum']; return $args['fromEnum'];
} }
} },
], ],
'complexEnum' => [ 'complexEnum' => [
'type' => $ComplexEnum, 'type' => $ComplexEnum,
@ -114,30 +123,31 @@ class EnumTypeTest extends TestCase
'type' => $ComplexEnum, 'type' => $ComplexEnum,
// Note: defaultValue is provided an *internal* representation for // Note: defaultValue is provided an *internal* representation for
// Enums, rather than the string name. // Enums, rather than the string name.
'defaultValue' => $Complex1 'defaultValue' => $Complex1,
], ],
'provideGoodValue' => [ 'provideGoodValue' => [
'type' => Type::boolean(), 'type' => Type::boolean(),
], ],
'provideBadValue' => [ 'provideBadValue' => [
'type' => Type::boolean() 'type' => Type::boolean(),
]
], ],
'resolve' => function($value, $args) use ($Complex1, $Complex2) { ],
if (!empty($args['provideGoodValue'])) { 'resolve' => function ($value, $args) use ($Complex1, $Complex2) {
if (! empty($args['provideGoodValue'])) {
// Note: this is one of the references of the internal values which // Note: this is one of the references of the internal values which
// ComplexEnum allows. // ComplexEnum allows.
return $Complex2; return $Complex2;
} }
if (!empty($args['provideBadValue'])) { if (! empty($args['provideBadValue'])) {
// Note: similar shape, but not the same *reference* // Note: similar shape, but not the same *reference*
// as Complex2 above. Enum internal values require === equality. // as Complex2 above. Enum internal values require === equality.
return new \ArrayObject(['someRandomValue' => 123]); return new \ArrayObject(['someRandomValue' => 123]);
} }
return $args['fromEnum']; return $args['fromEnum'];
} },
] ],
] ],
]); ]);
$MutationType = new ObjectType([ $MutationType = new ObjectType([
@ -147,10 +157,10 @@ class EnumTypeTest extends TestCase
'type' => $ColorType, 'type' => $ColorType,
'args' => ['color' => ['type' => $ColorType]], 'args' => ['color' => ['type' => $ColorType]],
'resolve' => function ($value, $args) { 'resolve' => function ($value, $args) {
return isset($args['color']) ? $args['color'] : null; return $args['color'] ?? null;
} },
] ],
] ],
]); ]);
$SubscriptionType = new ObjectType([ $SubscriptionType = new ObjectType([
@ -160,10 +170,10 @@ class EnumTypeTest extends TestCase
'type' => $ColorType, 'type' => $ColorType,
'args' => ['color' => ['type' => $ColorType]], 'args' => ['color' => ['type' => $ColorType]],
'resolve' => function ($value, $args) { 'resolve' => function ($value, $args) {
return isset($args['color']) ? $args['color'] : null; return $args['color'] ?? null;
} },
] ],
] ],
]); ]);
$this->Complex1 = $Complex1; $this->Complex1 = $Complex1;
@ -173,7 +183,7 @@ class EnumTypeTest extends TestCase
$this->schema = new Schema([ $this->schema = new Schema([
'query' => $QueryType, 'query' => $QueryType,
'mutation' => $MutationType, 'mutation' => $MutationType,
'subscription' => $SubscriptionType 'subscription' => $SubscriptionType,
]); ]);
} }
@ -221,12 +231,34 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: "GREEN") }', '{ colorEnum(fromEnum: "GREEN") }',
null, null,
[ [
'message' => "Expected type Color, found \"GREEN\"; Did you mean the enum value GREEN?", 'message' => 'Expected type Color, found "GREEN"; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)] 'locations' => [new SourceLocation(1, 23)],
] ]
); );
} }
private function expectFailure($query, $vars, $err)
{
$result = GraphQL::executeQuery($this->schema, $query, null, null, $vars);
$this->assertEquals(1, count($result->errors));
if (is_array($err)) {
$this->assertEquals(
$err['message'],
$result->errors[0]->getMessage()
);
$this->assertEquals(
$err['locations'],
$result->errors[0]->getLocations()
);
} else {
$this->assertEquals(
$err,
$result->errors[0]->getMessage()
);
}
}
/** /**
* @see it('does not accept valuesNotInTheEnum') * @see it('does not accept valuesNotInTheEnum')
*/ */
@ -236,8 +268,8 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: GREENISH) }', '{ colorEnum(fromEnum: GREENISH) }',
null, null,
[ [
'message' => "Expected type Color, found GREENISH; Did you mean the enum value GREEN?", 'message' => 'Expected type Color, found GREENISH; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)] 'locations' => [new SourceLocation(1, 23)],
] ]
); );
} }
@ -251,8 +283,8 @@ class EnumTypeTest extends TestCase
'{ colorEnum(fromEnum: green) }', '{ colorEnum(fromEnum: green) }',
null, null,
[ [
'message' => "Expected type Color, found green; Did you mean the enum value GREEN?", 'message' => 'Expected type Color, found green; Did you mean the enum value GREEN?',
'locations' => [new SourceLocation(1, 23)] 'locations' => [new SourceLocation(1, 23)],
] ]
); );
} }
@ -281,7 +313,7 @@ class EnumTypeTest extends TestCase
$this->expectFailure( $this->expectFailure(
'{ colorEnum(fromEnum: 1) }', '{ colorEnum(fromEnum: 1) }',
null, null,
"Expected type Color, found 1." 'Expected type Color, found 1.'
); );
} }
@ -293,7 +325,7 @@ class EnumTypeTest extends TestCase
$this->expectFailure( $this->expectFailure(
'{ colorEnum(fromInt: GREEN) }', '{ colorEnum(fromInt: GREEN) }',
null, null,
"Expected type Int, found GREEN." 'Expected type Int, found GREEN.'
); );
} }
@ -381,7 +413,7 @@ class EnumTypeTest extends TestCase
$this->expectFailure( $this->expectFailure(
'query test($color: Int!) { colorEnum(fromEnum: $color) }', 'query test($color: Int!) { colorEnum(fromEnum: $color) }',
['color' => 2], ['color' => 2],
'Variable "$color" of type "Int!" used in position ' . 'expecting type "Color".' 'Variable "$color" of type "Int!" used in position expecting type "Color".'
); );
} }
@ -392,10 +424,13 @@ class EnumTypeTest extends TestCase
{ {
$this->assertEquals( $this->assertEquals(
['data' => ['colorEnum' => 'RED', 'colorInt' => 0]], ['data' => ['colorEnum' => 'RED', 'colorInt' => 0]],
GraphQL::executeQuery($this->schema, "{ GraphQL::executeQuery(
$this->schema,
'{
colorEnum(fromEnum: RED) colorEnum(fromEnum: RED)
colorInt(fromEnum: RED) colorInt(fromEnum: RED)
}")->toArray() }'
)->toArray()
); );
} }
@ -406,10 +441,13 @@ class EnumTypeTest extends TestCase
{ {
$this->assertEquals( $this->assertEquals(
['data' => ['colorEnum' => null, 'colorInt' => null]], ['data' => ['colorEnum' => null, 'colorInt' => null]],
GraphQL::executeQuery($this->schema, "{ GraphQL::executeQuery(
$this->schema,
'{
colorEnum colorEnum
colorInt colorInt
}")->toArray() }'
)->toArray()
); );
} }
@ -446,25 +484,29 @@ class EnumTypeTest extends TestCase
*/ */
public function testMayBeInternallyRepresentedWithComplexValues() : void public function testMayBeInternallyRepresentedWithComplexValues() : void
{ {
$result = GraphQL::executeQuery($this->schema, '{ $result = GraphQL::executeQuery(
$this->schema,
'{
first: complexEnum first: complexEnum
second: complexEnum(fromEnum: TWO) second: complexEnum(fromEnum: TWO)
good: complexEnum(provideGoodValue: true) good: complexEnum(provideGoodValue: true)
bad: complexEnum(provideBadValue: true) bad: complexEnum(provideBadValue: true)
}')->toArray(true); }'
)->toArray(true);
$expected = [ $expected = [
'data' => [ 'data' => [
'first' => 'ONE', 'first' => 'ONE',
'second' => 'TWO', 'second' => 'TWO',
'good' => 'TWO', 'good' => 'TWO',
'bad' => null 'bad' => null,
], ],
'errors' => [[ 'errors' => [[
'debugMessage' => 'debugMessage' =>
'Expected a value of type "Complex" but received: instance of ArrayObject', 'Expected a value of type "Complex" but received: instance of ArrayObject',
'locations' => [['line' => 5, 'column' => 9]] 'locations' => [['line' => 5, 'column' => 9]],
]] ],
],
]; ];
$this->assertArraySubset($expected, $result); $this->assertArraySubset($expected, $result);
@ -492,32 +534,11 @@ class EnumTypeTest extends TestCase
'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null], 'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null],
'errors' => [[ 'errors' => [[
'debugMessage' => 'Expected a value of type "SimpleEnum" but received: WRONG', 'debugMessage' => 'Expected a value of type "SimpleEnum" but received: WRONG',
'locations' => [['line' => 4, 'column' => 13]] 'locations' => [['line' => 4, 'column' => 13]],
]] ],
],
], ],
GraphQL::executeQuery($this->schema, $q)->toArray(true) GraphQL::executeQuery($this->schema, $q)->toArray(true)
); );
} }
private function expectFailure($query, $vars, $err)
{
$result = GraphQL::executeQuery($this->schema, $query, null, null, $vars);
$this->assertEquals(1, count($result->errors));
if (is_array($err)) {
$this->assertEquals(
$err['message'],
$result->errors[0]->getMessage()
);
$this->assertEquals(
$err['locations'],
$result->errors[0]->getLocations()
);
} else {
$this->assertEquals(
$err,
$result->errors[0]->getMessage()
);
}
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
class ObjectIdStub class ObjectIdStub
{ {
/** /** @var int */
* @var int
*/
private $id; private $id;
/** /**

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
@ -10,75 +13,53 @@ use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\EagerResolution; use GraphQL\Type\EagerResolution;
use GraphQL\Type\LazyResolution; use GraphQL\Type\LazyResolution;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function lcfirst;
class ResolutionTest extends TestCase class ResolutionTest extends TestCase
{ {
/** /** @var ObjectType */
* @var ObjectType
*/
private $query; private $query;
/** /** @var ObjectType */
* @var ObjectType
*/
private $mutation; private $mutation;
/** /** @var InterfaceType */
* @var InterfaceType
*/
private $node; private $node;
/** /** @var InterfaceType */
* @var InterfaceType
*/
private $content; private $content;
/** /** @var ObjectType */
* @var ObjectType
*/
private $blogStory; private $blogStory;
/** /** @var ObjectType */
* @var ObjectType
*/
private $link;
/**
* @var ObjectType
*/
private $video; private $video;
/** /** @var ObjectType */
* @var ObjectType
*/
private $videoMetadata; private $videoMetadata;
/** /** @var ObjectType */
* @var ObjectType
*/
private $comment; private $comment;
/** /** @var ObjectType */
* @var ObjectType
*/
private $user; private $user;
/** /** @var ObjectType */
* @var ObjectType
*/
private $category; private $category;
/** /** @var UnionType */
* @var UnionType
*/
private $mention; private $mention;
/** @var ObjectType */
private $postStoryMutation; private $postStoryMutation;
/** @var InputObjectType */
private $postStoryMutationInput; private $postStoryMutationInput;
/** @var ObjectType */
private $postCommentMutation; private $postCommentMutation;
/** @var InputObjectType */
private $postCommentMutationInput; private $postCommentMutationInput;
public function setUp() public function setUp()
@ -86,48 +67,30 @@ class ResolutionTest extends TestCase
$this->node = new InterfaceType([ $this->node = new InterfaceType([
'name' => 'Node', 'name' => 'Node',
'fields' => [ 'fields' => [
'id' => Type::string() 'id' => Type::string(),
] ],
]); ]);
$this->content = new InterfaceType([ $this->content = new InterfaceType([
'name' => 'Content', 'name' => 'Content',
'fields' => function() { 'fields' => function () {
return [ return [
'title' => Type::string(), 'title' => Type::string(),
'body' => Type::string(), 'body' => Type::string(),
'author' => $this->user, 'author' => $this->user,
'comments' => Type::listOf($this->comment), 'comments' => Type::listOf($this->comment),
'categories' => Type::listOf($this->category) 'categories' => Type::listOf($this->category),
]; ];
} },
]); ]);
$this->blogStory = new ObjectType([ $this->blogStory = new ObjectType([
'name' => 'BlogStory', 'name' => 'BlogStory',
'interfaces' => [ 'interfaces' => [
$this->node, $this->node,
$this->content $this->content,
], ],
'fields' => function() { 'fields' => function () {
return [
$this->node->getField('id'),
$this->content->getField('title'),
$this->content->getField('body'),
$this->content->getField('author'),
$this->content->getField('comments'),
$this->content->getField('categories')
];
},
]);
$this->link = new ObjectType([
'name' => 'Link',
'interfaces' => [
$this->node,
$this->content
],
'fields' => function() {
return [ return [
$this->node->getField('id'), $this->node->getField('id'),
$this->content->getField('title'), $this->content->getField('title'),
@ -135,87 +98,108 @@ class ResolutionTest extends TestCase
$this->content->getField('author'), $this->content->getField('author'),
$this->content->getField('comments'), $this->content->getField('comments'),
$this->content->getField('categories'), $this->content->getField('categories'),
'url' => Type::string()
]; ];
}, },
]); ]);
new ObjectType([
'name' => 'Link',
'interfaces' => [
$this->node,
$this->content,
],
'fields' => function () {
return [
'id' => $this->node->getField('id'),
'title' => $this->content->getField('title'),
'body' => $this->content->getField('body'),
'author' => $this->content->getField('author'),
'comments' => $this->content->getField('comments'),
'categories' => $this->content->getField('categories'),
'url' => Type::string(),
];
},
]);
$this->videoMetadata = new ObjectType([
'name' => 'VideoMetadata',
'fields' => [
'lat' => Type::float(),
'lng' => Type::float(),
],
]);
$this->video = new ObjectType([ $this->video = new ObjectType([
'name' => 'Video', 'name' => 'Video',
'interfaces' => [ 'interfaces' => [
$this->node, $this->node,
$this->content $this->content,
], ],
'fields' => function() { 'fields' => function () {
return [ return [
$this->node->getField('id'), 'id' => $this->node->getField('id'),
$this->content->getField('title'), 'title' => $this->content->getField('title'),
$this->content->getField('body'), 'body' => $this->content->getField('body'),
$this->content->getField('author'), 'author' => $this->content->getField('author'),
$this->content->getField('comments'), 'comments' => $this->content->getField('comments'),
$this->content->getField('categories'), 'categories' => $this->content->getField('categories'),
'streamUrl' => Type::string(), 'streamUrl' => Type::string(),
'downloadUrl' => Type::string(), 'downloadUrl' => Type::string(),
'metadata' => $this->videoMetadata = new ObjectType([ 'metadata' => $this->videoMetadata,
'name' => 'VideoMetadata',
'fields' => [
'lat' => Type::float(),
'lng' => Type::float()
]
])
]; ];
} },
]); ]);
$this->comment = new ObjectType([ $this->comment = new ObjectType([
'name' => 'Comment', 'name' => 'Comment',
'interfaces' => [ 'interfaces' => [
$this->node $this->node,
], ],
'fields' => function() { 'fields' => function () {
return [ return [
$this->node->getField('id'), 'id' => $this->node->getField('id'),
'author' => $this->user, 'author' => $this->user,
'text' => Type::string(), 'text' => Type::string(),
'replies' => Type::listOf($this->comment), 'replies' => Type::listOf($this->comment),
'parent' => $this->comment, 'parent' => $this->comment,
'content' => $this->content 'content' => $this->content,
]; ];
} },
]); ]);
$this->user = new ObjectType([ $this->user = new ObjectType([
'name' => 'User', 'name' => 'User',
'interfaces' => [ 'interfaces' => [
$this->node $this->node,
], ],
'fields' => function() { 'fields' => function () {
return [ return [
$this->node->getField('id'), 'id' => $this->node->getField('id'),
'name' => Type::string(), 'name' => Type::string(),
]; ];
} },
]); ]);
$this->category = new ObjectType([ $this->category = new ObjectType([
'name' => 'Category', 'name' => 'Category',
'interfaces' => [ 'interfaces' => [
$this->node $this->node,
], ],
'fields' => function() { 'fields' => function () {
return [ return [
$this->node->getField('id'), 'id' => $this->node->getField('id'),
'name' => Type::string() 'name' => Type::string(),
]; ];
} },
]); ]);
$this->mention = new UnionType([ $this->mention = new UnionType([
'name' => 'Mention', 'name' => 'Mention',
'types' => [ 'types' => [
$this->user, $this->user,
$this->category $this->category,
] ],
]); ]);
$this->query = new ObjectType([ $this->query = new ObjectType([
@ -224,8 +208,18 @@ class ResolutionTest extends TestCase
'viewer' => $this->user, 'viewer' => $this->user,
'latestContent' => $this->content, 'latestContent' => $this->content,
'node' => $this->node, 'node' => $this->node,
'mentions' => Type::listOf($this->mention) 'mentions' => Type::listOf($this->mention),
] ],
]);
$this->postStoryMutationInput = new InputObjectType([
'name' => 'PostStoryMutationInput',
'fields' => [
'title' => Type::string(),
'body' => Type::string(),
'author' => Type::id(),
'category' => Type::id(),
],
]); ]);
$this->mutation = new ObjectType([ $this->mutation = new ObjectType([
@ -235,28 +229,20 @@ class ResolutionTest extends TestCase
'type' => $this->postStoryMutation = new ObjectType([ 'type' => $this->postStoryMutation = new ObjectType([
'name' => 'PostStoryMutation', 'name' => 'PostStoryMutation',
'fields' => [ 'fields' => [
'story' => $this->blogStory 'story' => $this->blogStory,
] ],
]), ]),
'args' => [ 'args' => [
'input' => Type::nonNull($this->postStoryMutationInput = new InputObjectType([ 'input' => Type::nonNull($this->postStoryMutationInput),
'name' => 'PostStoryMutationInput', 'clientRequestId' => Type::string(),
'fields' => [ ],
'title' => Type::string(),
'body' => Type::string(),
'author' => Type::id(),
'category' => Type::id()
]
])),
'clientRequestId' => Type::string()
]
], ],
'postComment' => [ 'postComment' => [
'type' => $this->postCommentMutation = new ObjectType([ 'type' => $this->postCommentMutation = new ObjectType([
'name' => 'PostCommentMutation', 'name' => 'PostCommentMutation',
'fields' => [ 'fields' => [
'comment' => $this->comment 'comment' => $this->comment,
] ],
]), ]),
'args' => [ 'args' => [
'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([ 'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([
@ -265,13 +251,13 @@ class ResolutionTest extends TestCase
'text' => Type::nonNull(Type::string()), 'text' => Type::nonNull(Type::string()),
'author' => Type::nonNull(Type::id()), 'author' => Type::nonNull(Type::id()),
'content' => Type::id(), 'content' => Type::id(),
'parent' => Type::id() 'parent' => Type::id(),
] ],
])), ])),
'clientRequestId' => Type::string() 'clientRequestId' => Type::string(),
] ],
] ],
] ],
]); ]);
} }
@ -284,7 +270,7 @@ class ResolutionTest extends TestCase
'String' => Type::string(), 'String' => Type::string(),
'Float' => Type::float(), 'Float' => Type::float(),
'Int' => Type::int(), 'Int' => Type::int(),
'Boolean' => Type::boolean() 'Boolean' => Type::boolean(),
]; ];
$this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap());
@ -297,7 +283,7 @@ class ResolutionTest extends TestCase
'Int' => 1, 'Int' => 1,
'Boolean' => 1, 'Boolean' => 1,
], ],
'possibleTypeMap' => [] 'possibleTypeMap' => [],
]; ];
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
@ -321,10 +307,16 @@ class ResolutionTest extends TestCase
$this->assertSame($this->postStoryMutation, $eagerTypeResolution->resolveType('PostStoryMutation')); $this->assertSame($this->postStoryMutation, $eagerTypeResolution->resolveType('PostStoryMutation'));
$this->assertSame($this->postStoryMutationInput, $eagerTypeResolution->resolveType('PostStoryMutationInput')); $this->assertSame($this->postStoryMutationInput, $eagerTypeResolution->resolveType('PostStoryMutationInput'));
$this->assertSame($this->postCommentMutation, $eagerTypeResolution->resolveType('PostCommentMutation')); $this->assertSame($this->postCommentMutation, $eagerTypeResolution->resolveType('PostCommentMutation'));
$this->assertSame($this->postCommentMutationInput, $eagerTypeResolution->resolveType('PostCommentMutationInput')); $this->assertSame(
$this->postCommentMutationInput,
$eagerTypeResolution->resolveType('PostCommentMutationInput')
);
$this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content)); $this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content));
$this->assertEquals([$this->user, $this->comment, $this->category, $this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->node)); $this->assertEquals(
[$this->user, $this->comment, $this->category, $this->blogStory],
$eagerTypeResolution->resolvePossibleTypes($this->node)
);
$this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention));
$expectedTypeMap = [ $expectedTypeMap = [
@ -345,7 +337,7 @@ class ResolutionTest extends TestCase
'PostCommentMutation' => $this->postCommentMutation, 'PostCommentMutation' => $this->postCommentMutation,
'Float' => Type::float(), 'Float' => Type::float(),
'Int' => Type::int(), 'Int' => Type::int(),
'Boolean' => Type::boolean() 'Boolean' => Type::boolean(),
]; ];
$this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap());
@ -370,23 +362,21 @@ class ResolutionTest extends TestCase
'PostCommentMutation' => 1, 'PostCommentMutation' => 1,
'Float' => 1, 'Float' => 1,
'Int' => 1, 'Int' => 1,
'Boolean' => 1 'Boolean' => 1,
], ],
'possibleTypeMap' => [ 'possibleTypeMap' => [
'Node' => [ 'Node' => [
'User' => 1, 'User' => 1,
'Comment' => 1, 'Comment' => 1,
'Category' => 1, 'Category' => 1,
'BlogStory' => 1 'BlogStory' => 1,
],
'Content' => [
'BlogStory' => 1
], ],
'Content' => ['BlogStory' => 1],
'Mention' => [ 'Mention' => [
'User' => 1, 'User' => 1,
'Category' => 1 'Category' => 1,
] ],
] ],
]; ];
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
@ -402,7 +392,10 @@ class ResolutionTest extends TestCase
$this->assertEquals(null, $eagerTypeResolution->resolveType('VideoMetadata')); $this->assertEquals(null, $eagerTypeResolution->resolveType('VideoMetadata'));
$this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content)); $this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content));
$this->assertEquals([$this->user, $this->comment, $this->category, $this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->node)); $this->assertEquals(
[$this->user, $this->comment, $this->category, $this->blogStory],
$eagerTypeResolution->resolvePossibleTypes($this->node)
);
$this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention));
$eagerTypeResolution = new EagerResolution([null, $this->video, null]); $eagerTypeResolution = new EagerResolution([null, $this->video, null]);
@ -410,7 +403,10 @@ class ResolutionTest extends TestCase
$this->assertEquals($this->video, $eagerTypeResolution->resolveType('Video')); $this->assertEquals($this->video, $eagerTypeResolution->resolveType('Video'));
$this->assertEquals([$this->video], $eagerTypeResolution->resolvePossibleTypes($this->content)); $this->assertEquals([$this->video], $eagerTypeResolution->resolvePossibleTypes($this->content));
$this->assertEquals([$this->video, $this->user, $this->comment, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->node)); $this->assertEquals(
[$this->video, $this->user, $this->comment, $this->category],
$eagerTypeResolution->resolvePossibleTypes($this->node)
);
$this->assertEquals([], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $this->assertEquals([], $eagerTypeResolution->resolvePossibleTypes($this->mention));
$expectedTypeMap = [ $expectedTypeMap = [
@ -425,7 +421,7 @@ class ResolutionTest extends TestCase
'Float' => Type::float(), 'Float' => Type::float(),
'ID' => Type::id(), 'ID' => Type::id(),
'Int' => Type::int(), 'Int' => Type::int(),
'Boolean' => Type::boolean() 'Boolean' => Type::boolean(),
]; ];
$this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap());
@ -443,19 +439,17 @@ class ResolutionTest extends TestCase
'Float' => 1, 'Float' => 1,
'ID' => 1, 'ID' => 1,
'Int' => 1, 'Int' => 1,
'Boolean' => 1 'Boolean' => 1,
], ],
'possibleTypeMap' => [ 'possibleTypeMap' => [
'Node' => [ 'Node' => [
'Video' => 1, 'Video' => 1,
'User' => 1, 'User' => 1,
'Comment' => 1, 'Comment' => 1,
'Category' => 1 'Category' => 1,
],
'Content' => ['Video' => 1],
], ],
'Content' => [
'Video' => 1
]
]
]; ];
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
} }
@ -466,8 +460,8 @@ class ResolutionTest extends TestCase
$eager = new EagerResolution([]); $eager = new EagerResolution([]);
$emptyDescriptor = $eager->getDescriptor(); $emptyDescriptor = $eager->getDescriptor();
$typeLoader = function($name) { $typeLoader = function ($name) {
throw new \Exception("This should be never called for empty descriptor"); throw new \Exception('This should be never called for empty descriptor');
}; };
$lazy = new LazyResolution($emptyDescriptor, $typeLoader); $lazy = new LazyResolution($emptyDescriptor, $typeLoader);
@ -480,9 +474,10 @@ class ResolutionTest extends TestCase
$called = 0; $called = 0;
$descriptor = $eager->getDescriptor(); $descriptor = $eager->getDescriptor();
$typeLoader = function($name) use (&$called) { $typeLoader = function ($name) use (&$called) {
$called++; $called++;
$prop = lcfirst($name); $prop = lcfirst($name);
return $this->{$prop}; return $this->{$prop};
}; };
@ -507,7 +502,10 @@ class ResolutionTest extends TestCase
$this->assertSame($eager->resolveType('PostStoryMutation'), $lazy->resolveType('PostStoryMutation')); $this->assertSame($eager->resolveType('PostStoryMutation'), $lazy->resolveType('PostStoryMutation'));
$this->assertSame($eager->resolveType('PostStoryMutationInput'), $lazy->resolveType('PostStoryMutationInput')); $this->assertSame($eager->resolveType('PostStoryMutationInput'), $lazy->resolveType('PostStoryMutationInput'));
$this->assertSame($eager->resolveType('PostCommentMutation'), $lazy->resolveType('PostCommentMutation')); $this->assertSame($eager->resolveType('PostCommentMutation'), $lazy->resolveType('PostCommentMutation'));
$this->assertSame($eager->resolveType('PostCommentMutationInput'), $lazy->resolveType('PostCommentMutationInput')); $this->assertSame(
$eager->resolveType('PostCommentMutationInput'),
$lazy->resolveType('PostCommentMutationInput')
);
$this->assertSame(13, $called); $this->assertSame(13, $called);
$this->assertEquals($eager->resolvePossibleTypes($this->content), $lazy->resolvePossibleTypes($this->content)); $this->assertEquals($eager->resolvePossibleTypes($this->content), $lazy->resolvePossibleTypes($this->content));
@ -527,25 +525,29 @@ class ResolutionTest extends TestCase
$this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention)); $this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention));
} }
private function createLazy(){ public function testLazyThrowsOnInvalidLoadedType() : void
{
$lazy = $this->createLazy();
$this->expectException(InvariantViolation::class);
$this->expectExceptionMessage('Lazy Type Resolution Error: Expecting GraphQL Type instance, but got integer');
$lazy->resolveType('int');
}
private function createLazy()
{
$descriptor = [ $descriptor = [
'version' => '1.0', 'version' => '1.0',
'typeMap' => [ 'typeMap' => [
'null' => 1, 'null' => 1,
'int' => 1 'int' => 1,
], ],
'possibleTypeMap' => [ 'possibleTypeMap' => [
'a' => [ 'a' => ['null' => 1],
'null' => 1, 'b' => ['int' => 1],
], ],
'b' => [
'int' => 1
]
]
]; ];
$invalidTypeLoader = function($name) { $invalidTypeLoader = function ($name) {
switch ($name) { switch ($name) {
case 'null': case 'null':
return null; return null;
@ -561,14 +563,6 @@ class ResolutionTest extends TestCase
return $lazy; return $lazy;
} }
public function testLazyThrowsOnInvalidLoadedType() : void
{
$lazy = $this->createLazy();
$this->expectException(InvariantViolation::class);
$this->expectExceptionMessage('Lazy Type Resolution Error: Expecting GraphQL Type instance, but got integer');
$lazy->resolveType('int');
}
public function testLazyThrowsOnInvalidLoadedPossibleType() : void public function testLazyThrowsOnInvalidLoadedPossibleType() : void
{ {
$tmp = new InterfaceType(['name' => 'a', 'fields' => []]); $tmp = new InterfaceType(['name' => 'a', 'fields' => []]);

View File

@ -1,11 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
use GraphQL\GraphQL; use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class ResolveInfoTest extends TestCase class ResolveInfoTest extends TestCase
@ -17,22 +20,25 @@ class ResolveInfoTest extends TestCase
'fields' => [ 'fields' => [
'url' => ['type' => Type::string()], 'url' => ['type' => Type::string()],
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
] ],
]); ]);
$article = null; $article = null;
$author = new ObjectType([ $author = new ObjectType([
'name' => 'Author', 'name' => 'Author',
'fields' => function() use ($image, &$article) { 'fields' => function () use ($image, &$article) {
return [ return [
'id' => ['type' => Type::string()], 'id' => ['type' => Type::string()],
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'pic' => [ 'type' => $image, 'args' => [ 'pic' => [
'type' => $image,
'args' => [
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
]], ],
],
'recentArticle' => ['type' => $article], 'recentArticle' => ['type' => $article],
]; ];
}, },
@ -42,8 +48,8 @@ class ResolveInfoTest extends TestCase
'name' => 'Reply', 'name' => 'Reply',
'fields' => [ 'fields' => [
'author' => ['type' => $author], 'author' => ['type' => $author],
'body' => ['type' => Type::string()] 'body' => ['type' => Type::string()],
] ],
]); ]);
$article = new ObjectType([ $article = new ObjectType([
@ -55,8 +61,8 @@ class ResolveInfoTest extends TestCase
'title' => ['type' => Type::string()], 'title' => ['type' => Type::string()],
'body' => ['type' => Type::string()], 'body' => ['type' => Type::string()],
'image' => ['type' => $image], 'image' => ['type' => $image],
'replies' => ['type' => Type::listOf($reply)] 'replies' => ['type' => Type::listOf($reply)],
] ],
]); ]);
$doc = ' $doc = '
@ -102,20 +108,20 @@ class ResolveInfoTest extends TestCase
$expectedDefaultSelection = [ $expectedDefaultSelection = [
'author' => true, 'author' => true,
'image' => true, 'image' => true,
'replies' => true 'replies' => true,
]; ];
$expectedDeepSelection = [ $expectedDeepSelection = [
'author' => [ 'author' => [
'name' => true, 'name' => true,
'pic' => [ 'pic' => [
'url' => true, 'url' => true,
'width' => true 'width' => true,
] ],
], ],
'image' => [ 'image' => [
'width' => true, 'width' => true,
'height' => true, 'height' => true,
'url' => true 'url' => true,
], ],
'replies' => [ 'replies' => [
'body' => true, 'body' => true,
@ -125,15 +131,15 @@ class ResolveInfoTest extends TestCase
'pic' => [ 'pic' => [
'url' => true, 'url' => true,
'width' => true, 'width' => true,
'height' => true 'height' => true,
], ],
'recentArticle' => [ 'recentArticle' => [
'id' => true, 'id' => true,
'title' => true, 'title' => true,
'body' => true 'body' => true,
] ],
] ],
] ],
]; ];
$hasCalled = false; $hasCalled = false;
@ -145,14 +151,25 @@ class ResolveInfoTest extends TestCase
'fields' => [ 'fields' => [
'article' => [ 'article' => [
'type' => $article, 'type' => $article,
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDefaultSelection, &$actualDeepSelection) { 'resolve' => function (
$value,
$args,
$context,
ResolveInfo $info
) use (
&$hasCalled,
&
$actualDefaultSelection,
&$actualDeepSelection
) {
$hasCalled = true; $hasCalled = true;
$actualDefaultSelection = $info->getFieldSelection(); $actualDefaultSelection = $info->getFieldSelection();
$actualDeepSelection = $info->getFieldSelection(5); $actualDeepSelection = $info->getFieldSelection(5);
return null; return null;
} },
] ],
] ],
]); ]);
$schema = new Schema(['query' => $blogQuery]); $schema = new Schema(['query' => $blogQuery]);
@ -171,22 +188,25 @@ class ResolveInfoTest extends TestCase
'fields' => [ 'fields' => [
'url' => ['type' => Type::string()], 'url' => ['type' => Type::string()],
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
] ],
]); ]);
$article = null; $article = null;
$author = new ObjectType([ $author = new ObjectType([
'name' => 'Author', 'name' => 'Author',
'fields' => function() use ($image, &$article) { 'fields' => function () use ($image, &$article) {
return [ return [
'id' => ['type' => Type::string()], 'id' => ['type' => Type::string()],
'name' => ['type' => Type::string()], 'name' => ['type' => Type::string()],
'pic' => [ 'type' => $image, 'args' => [ 'pic' => [
'type' => $image,
'args' => [
'width' => ['type' => Type::int()], 'width' => ['type' => Type::int()],
'height' => ['type' => Type::int()] 'height' => ['type' => Type::int()],
]], ],
],
'recentArticle' => ['type' => $article], 'recentArticle' => ['type' => $article],
]; ];
}, },
@ -196,8 +216,8 @@ class ResolveInfoTest extends TestCase
'name' => 'Reply', 'name' => 'Reply',
'fields' => [ 'fields' => [
'author' => ['type' => $author], 'author' => ['type' => $author],
'body' => ['type' => Type::string()] 'body' => ['type' => Type::string()],
] ],
]); ]);
$article = new ObjectType([ $article = new ObjectType([
@ -209,8 +229,8 @@ class ResolveInfoTest extends TestCase
'title' => ['type' => Type::string()], 'title' => ['type' => Type::string()],
'body' => ['type' => Type::string()], 'body' => ['type' => Type::string()],
'image' => ['type' => $image], 'image' => ['type' => $image],
'replies' => ['type' => Type::listOf($reply)] 'replies' => ['type' => Type::listOf($reply)],
] ],
]); ]);
$doc = ' $doc = '
@ -268,13 +288,13 @@ class ResolveInfoTest extends TestCase
'name' => true, 'name' => true,
'pic' => [ 'pic' => [
'url' => true, 'url' => true,
'width' => true 'width' => true,
] ],
], ],
'image' => [ 'image' => [
'width' => true, 'width' => true,
'height' => true, 'height' => true,
'url' => true 'url' => true,
], ],
'replies' => [ 'replies' => [
'body' => true, //this would be missing if not for the fix https://github.com/webonyx/graphql-php/pull/98 'body' => true, //this would be missing if not for the fix https://github.com/webonyx/graphql-php/pull/98
@ -284,15 +304,15 @@ class ResolveInfoTest extends TestCase
'pic' => [ 'pic' => [
'url' => true, 'url' => true,
'width' => true, 'width' => true,
'height' => true 'height' => true,
], ],
'recentArticle' => [ 'recentArticle' => [
'id' => true, 'id' => true,
'title' => true, 'title' => true,
'body' => true 'body' => true,
] ],
] ],
] ],
]; ];
$hasCalled = false; $hasCalled = false;
@ -304,13 +324,23 @@ class ResolveInfoTest extends TestCase
'fields' => [ 'fields' => [
'article' => [ 'article' => [
'type' => $article, 'type' => $article,
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDeepSelection) { 'resolve' => function (
$value,
$args,
$context,
ResolveInfo $info
) use (
&$hasCalled,
&
$actualDeepSelection
) {
$hasCalled = true; $hasCalled = true;
$actualDeepSelection = $info->getFieldSelection(5); $actualDeepSelection = $info->getFieldSelection(5);
return null; return null;
} },
] ],
] ],
]); ]);
$schema = new Schema(['query' => $blogQuery]); $schema = new Schema(['query' => $blogQuery]);
@ -320,6 +350,4 @@ class ResolveInfoTest extends TestCase
$this->assertEquals(['data' => ['article' => null]], $result); $this->assertEquals(['data' => ['article' => null]], $result);
$this->assertEquals($expectedDeepSelection, $actualDeepSelection); $this->assertEquals($expectedDeepSelection, $actualDeepSelection);
} }
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
use GraphQL\Error\Error; use GraphQL\Error\Error;
@ -8,7 +11,6 @@ use PHPUnit\Framework\TestCase;
class ScalarSerializationTest extends TestCase class ScalarSerializationTest extends TestCase
{ {
// Type System: Scalar coercion // Type System: Scalar coercion
/** /**
* @see it('serializes output int') * @see it('serializes output int')
*/ */
@ -42,7 +44,6 @@ class ScalarSerializationTest extends TestCase
$this->expectException(Error::class); $this->expectException(Error::class);
$this->expectExceptionMessage('Int cannot represent non-integer value: 1.1'); $this->expectExceptionMessage('Int cannot represent non-integer value: 1.1');
$intType->serialize(1.1); $intType->serialize(1.1);
} }
public function testSerializesOutputIntCannotRepresentNegativeFloat() : void public function testSerializesOutputIntCannotRepresentNegativeFloat() : void
@ -51,7 +52,6 @@ class ScalarSerializationTest extends TestCase
$this->expectException(Error::class); $this->expectException(Error::class);
$this->expectExceptionMessage('Int cannot represent non-integer value: -1.1'); $this->expectExceptionMessage('Int cannot represent non-integer value: -1.1');
$intType->serialize(-1.1); $intType->serialize(-1.1);
} }
public function testSerializesOutputIntCannotRepresentNumericString() : void public function testSerializesOutputIntCannotRepresentNumericString() : void
@ -60,7 +60,6 @@ class ScalarSerializationTest extends TestCase
$this->expectException(Error::class); $this->expectException(Error::class);
$this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: Int cannot represent non-integer value: "-1.1"'); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: Int cannot represent non-integer value: "-1.1"');
$intType->serialize('Int cannot represent non-integer value: "-1.1"'); $intType->serialize('Int cannot represent non-integer value: "-1.1"');
} }
public function testSerializesOutputIntCannotRepresentBiggerThan32Bits() : void public function testSerializesOutputIntCannotRepresentBiggerThan32Bits() : void
@ -71,7 +70,6 @@ class ScalarSerializationTest extends TestCase
$this->expectException(Error::class); $this->expectException(Error::class);
$this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 9876504321'); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 9876504321');
$intType->serialize(9876504321); $intType->serialize(9876504321);
} }
public function testSerializesOutputIntCannotRepresentLowerThan32Bits() : void public function testSerializesOutputIntCannotRepresentLowerThan32Bits() : void
@ -104,7 +102,6 @@ class ScalarSerializationTest extends TestCase
$this->expectException(Error::class); $this->expectException(Error::class);
$this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: one'); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: one');
$intType->serialize('one'); $intType->serialize('one');
} }
public function testSerializesOutputIntCannotRepresentEmptyString() : void public function testSerializesOutputIntCannotRepresentEmptyString() : void
@ -196,7 +193,6 @@ class ScalarSerializationTest extends TestCase
$this->assertSame(false, $boolType->serialize(0)); $this->assertSame(false, $boolType->serialize(0));
$this->assertSame(true, $boolType->serialize(true)); $this->assertSame(true, $boolType->serialize(true));
$this->assertSame(false, $boolType->serialize(false)); $this->assertSame(false, $boolType->serialize(false));
// TODO: how should it behave on '0'? // TODO: how should it behave on '0'?
} }

View File

@ -1,4 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace GraphQL\Tests\Type; namespace GraphQL\Tests\Type;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
@ -12,14 +15,19 @@ use PHPUnit\Framework\TestCase;
class SchemaTest extends TestCase class SchemaTest extends TestCase
{ {
/** @var InterfaceType */
private $interfaceType; private $interfaceType;
/** @var ObjectType */
private $implementingType; private $implementingType;
/** @var InputObjectType */
private $directiveInputType; private $directiveInputType;
/** @var InputObjectType */
private $wrappedDirectiveInputType; private $wrappedDirectiveInputType;
/** @var Directive */
private $directive; private $directive;
/** @var Schema */ /** @var Schema */
@ -35,9 +43,14 @@ class SchemaTest extends TestCase
$this->implementingType = new ObjectType([ $this->implementingType = new ObjectType([
'name' => 'Object', 'name' => 'Object',
'interfaces' => [$this->interfaceType], 'interfaces' => [$this->interfaceType],
'fields' => ['fieldName' => ['type' => Type::string(), 'resolve' => function () { 'fields' => [
'fieldName' => [
'type' => Type::string(),
'resolve' => function () {
return ''; return '';
}]], },
],
],
]); ]);
$this->directiveInputType = new InputObjectType([ $this->directiveInputType = new InputObjectType([

View File

@ -1,32 +0,0 @@
<?php
namespace GraphQL\Tests\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
class MyCustomType extends ObjectType
{
public function __construct()
{
$config = [
'fields' => [
'a' => Type::string()
]
];
parent::__construct($config);
}
}
// Note: named OtherCustom vs OtherCustomType intentionally
class OtherCustom extends ObjectType
{
public function __construct()
{
$config = [
'fields' => [
'b' => Type::string()
]
];
parent::__construct($config);
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Type\TestClasses;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
class MyCustomType extends ObjectType
{
public function __construct()
{
$config = [
'fields' => [
'a' => Type::string(),
],
];
parent::__construct($config);
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace GraphQL\Tests\Type\TestClasses;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
/**
* Note: named OtherCustom vs OtherCustomType intentionally
*/
class OtherCustom extends ObjectType
{
public function __construct()
{
$config = [
'fields' => [
'b' => Type::string(),
],
];
parent::__construct($config);
}
}

View File

@ -1,6 +1,8 @@
<?php <?php
namespace GraphQL\Tests\Type;
declare(strict_types=1);
namespace GraphQL\Tests\Type;
use GraphQL\Error\InvariantViolation; use GraphQL\Error\InvariantViolation;
use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\InputObjectType;
@ -9,52 +11,35 @@ use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema; use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use function lcfirst;
class TypeLoaderTest extends TestCase class TypeLoaderTest extends TestCase
{ {
/** /** @var ObjectType */
* @var ObjectType
*/
private $query; private $query;
/** /** @var ObjectType */
* @var ObjectType
*/
private $mutation; private $mutation;
/** /** @var InterfaceType */
* @var InterfaceType
*/
private $node; private $node;
/** /** @var InterfaceType */
* @var InterfaceType
*/
private $content; private $content;
/** /** @var ObjectType */
* @var ObjectType
*/
private $blogStory; private $blogStory;
/** /** @var ObjectType */
* @var ObjectType
*/
private $postStoryMutation; private $postStoryMutation;
/** /** @var InputObjectType */
* @var InputObjectType
*/
private $postStoryMutationInput; private $postStoryMutationInput;
/** /** @var callable */
* @var callable
*/
private $typeLoader; private $typeLoader;
/** /** @var string[] */
* @var array
*/
private $calls; private $calls;
public function setUp() public function setUp()
@ -63,35 +48,40 @@ class TypeLoaderTest extends TestCase
$this->node = new InterfaceType([ $this->node = new InterfaceType([
'name' => 'Node', 'name' => 'Node',
'fields' => function() { 'fields' => function () {
$this->calls[] = 'Node.fields'; $this->calls[] = 'Node.fields';
return [ return [
'id' => Type::string() 'id' => Type::string(),
]; ];
}, },
'resolveType' => function() {} 'resolveType' => function () {
},
]); ]);
$this->content = new InterfaceType([ $this->content = new InterfaceType([
'name' => 'Content', 'name' => 'Content',
'fields' => function() { 'fields' => function () {
$this->calls[] = 'Content.fields'; $this->calls[] = 'Content.fields';
return [ return [
'title' => Type::string(), 'title' => Type::string(),
'body' => Type::string(), 'body' => Type::string(),
]; ];
}, },
'resolveType' => function() {} 'resolveType' => function () {
},
]); ]);
$this->blogStory = new ObjectType([ $this->blogStory = new ObjectType([
'name' => 'BlogStory', 'name' => 'BlogStory',
'interfaces' => [ 'interfaces' => [
$this->node, $this->node,
$this->content $this->content,
], ],
'fields' => function() { 'fields' => function () {
$this->calls[] = 'BlogStory.fields'; $this->calls[] = 'BlogStory.fields';
return [ return [
$this->node->getField('id'), $this->node->getField('id'),
$this->content->getField('title'), $this->content->getField('title'),
@ -102,36 +92,38 @@ class TypeLoaderTest extends TestCase
$this->query = new ObjectType([ $this->query = new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => function() { 'fields' => function () {
$this->calls[] = 'Query.fields'; $this->calls[] = 'Query.fields';
return [ return [
'latestContent' => $this->content, 'latestContent' => $this->content,
'node' => $this->node, 'node' => $this->node,
]; ];
} },
]); ]);
$this->mutation = new ObjectType([ $this->mutation = new ObjectType([
'name' => 'Mutation', 'name' => 'Mutation',
'fields' => function() { 'fields' => function () {
$this->calls[] = 'Mutation.fields'; $this->calls[] = 'Mutation.fields';
return [ return [
'postStory' => [ 'postStory' => [
'type' => $this->postStoryMutation, 'type' => $this->postStoryMutation,
'args' => [ 'args' => [
'input' => Type::nonNull($this->postStoryMutationInput), 'input' => Type::nonNull($this->postStoryMutationInput),
'clientRequestId' => Type::string() 'clientRequestId' => Type::string(),
] ],
] ],
]; ];
} },
]); ]);
$this->postStoryMutation = new ObjectType([ $this->postStoryMutation = new ObjectType([
'name' => 'PostStoryMutation', 'name' => 'PostStoryMutation',
'fields' => [ 'fields' => [
'story' => $this->blogStory 'story' => $this->blogStory,
] ],
]); ]);
$this->postStoryMutationInput = new InputObjectType([ $this->postStoryMutationInput = new InputObjectType([
@ -140,14 +132,15 @@ class TypeLoaderTest extends TestCase
'title' => Type::string(), 'title' => Type::string(),
'body' => Type::string(), 'body' => Type::string(),
'author' => Type::id(), 'author' => Type::id(),
'category' => Type::id() 'category' => Type::id(),
] ],
]); ]);
$this->typeLoader = function($name) { $this->typeLoader = function ($name) {
$this->calls[] = $name; $this->calls[] = $name;
$prop = lcfirst($name); $prop = lcfirst($name);
return isset($this->{$prop}) ? $this->{$prop} : null;
return $this->{$prop} ?? null;
}; };
} }
@ -157,9 +150,10 @@ class TypeLoaderTest extends TestCase
new Schema([ new Schema([
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => ['a' => Type::string()] 'fields' => ['a' => Type::string()],
]), ]),
'typeLoader' => function() {} 'typeLoader' => function () {
},
]); ]);
} }
@ -171,9 +165,9 @@ class TypeLoaderTest extends TestCase
new Schema([ new Schema([
'query' => new ObjectType([ 'query' => new ObjectType([
'name' => 'Query', 'name' => 'Query',
'fields' => ['a' => Type::string()] 'fields' => ['a' => Type::string()],
]), ]),
'typeLoader' => [] 'typeLoader' => [],
]); ]);
} }
@ -182,7 +176,7 @@ class TypeLoaderTest extends TestCase
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'mutation' => $this->mutation, 'mutation' => $this->mutation,
'types' => [$this->blogStory] 'types' => [$this->blogStory],
]); ]);
$expected = [ $expected = [
@ -220,7 +214,7 @@ class TypeLoaderTest extends TestCase
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'mutation' => $this->mutation, 'mutation' => $this->mutation,
'typeLoader' => $this->typeLoader 'typeLoader' => $this->typeLoader,
]); ]);
$this->assertEquals([], $this->calls); $this->assertEquals([], $this->calls);
@ -245,7 +239,7 @@ class TypeLoaderTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'typeLoader' => $this->typeLoader 'typeLoader' => $this->typeLoader,
]); ]);
$schema->getType('Node'); $schema->getType('Node');
@ -259,7 +253,8 @@ class TypeLoaderTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'typeLoader' => function() {} 'typeLoader' => function () {
},
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
@ -272,9 +267,9 @@ class TypeLoaderTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'typeLoader' => function() { 'typeLoader' => function () {
return new \stdClass(); return new \stdClass();
} },
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
@ -287,9 +282,9 @@ class TypeLoaderTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'typeLoader' => function() { 'typeLoader' => function () {
return $this->content; return $this->content;
} },
]); ]);
$this->expectException(InvariantViolation::class); $this->expectException(InvariantViolation::class);
@ -302,9 +297,9 @@ class TypeLoaderTest extends TestCase
{ {
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'typeLoader' => function() { 'typeLoader' => function () {
throw new \Exception("This is the exception we are looking for"); throw new \Exception('This is the exception we are looking for');
} },
]); ]);
$this->expectException(\Throwable::class); $this->expectException(\Throwable::class);
@ -317,13 +312,13 @@ class TypeLoaderTest extends TestCase
{ {
$withoutLoader = new Schema([ $withoutLoader = new Schema([
'query' => $this->query, 'query' => $this->query,
'mutation' => $this->mutation 'mutation' => $this->mutation,
]); ]);
$withLoader = new Schema([ $withLoader = new Schema([
'query' => $this->query, 'query' => $this->query,
'mutation' => $this->mutation, 'mutation' => $this->mutation,
'typeLoader' => $this->typeLoader 'typeLoader' => $this->typeLoader,
]); ]);
$this->assertSame($withoutLoader->getQueryType(), $withLoader->getQueryType()); $this->assertSame($withoutLoader->getQueryType(), $withLoader->getQueryType());
@ -337,7 +332,7 @@ class TypeLoaderTest extends TestCase
$schema = new Schema([ $schema = new Schema([
'query' => $this->query, 'query' => $this->query,
'mutation' => $this->mutation, 'mutation' => $this->mutation,
'typeLoader' => $this->typeLoader 'typeLoader' => $this->typeLoader,
]); ]);
$type = $schema->getType('ID'); $type = $schema->getType('ID');

File diff suppressed because it is too large Load Diff