graphql-php/tests/Utils/BuildSchemaTest.php

1302 lines
30 KiB
PHP
Raw Permalink Normal View History

2017-02-19 22:26:56 +03:00
<?php
2018-09-02 13:44:21 +03:00
declare(strict_types=1);
2017-02-19 22:26:56 +03:00
namespace GraphQL\Tests\Utils;
2018-09-26 12:07:23 +03:00
use Closure;
2018-07-29 18:43:10 +03:00
use GraphQL\Error\Error;
2017-02-19 22:26:56 +03:00
use GraphQL\GraphQL;
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
2017-02-19 22:26:56 +03:00
use GraphQL\Language\Parser;
use GraphQL\Language\Printer;
2018-09-02 13:44:21 +03:00
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\ObjectType;
2017-02-19 22:26:56 +03:00
use GraphQL\Utils\BuildSchema;
use GraphQL\Utils\SchemaPrinter;
2018-07-29 18:43:10 +03:00
use PHPUnit\Framework\TestCase;
2018-09-02 13:44:21 +03:00
use function array_keys;
use function count;
2017-02-19 22:26:56 +03:00
2018-07-29 18:43:10 +03:00
class BuildSchemaTest extends TestCase
2017-02-19 22:26:56 +03:00
{
// Describe: Schema Builder
/**
* @see it('can use built schema for limited execution')
2017-02-19 22:26:56 +03:00
*/
public function testUseBuiltSchemaForLimitedExecution() : void
2017-02-19 22:26:56 +03:00
{
$schema = BuildSchema::buildAST(Parser::parse('
type Query {
str: String
}
'));
$result = GraphQL::executeQuery($schema, '{ str }', ['str' => 123]);
2018-09-19 18:12:09 +03:00
self::assertEquals(['str' => 123], $result->toArray(true)['data']);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('can build a schema directly from the source')
2017-02-19 22:26:56 +03:00
*/
public function testBuildSchemaDirectlyFromSource() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$schema = BuildSchema::build('
2017-02-19 22:26:56 +03:00
type Query {
add(x: Int, y: Int): Int
}
2018-09-02 13:44:21 +03:00
');
2017-02-19 22:26:56 +03:00
2018-08-22 12:14:47 +03:00
$root = [
2019-06-30 21:54:56 +03:00
'add' => static function ($rootValue, $args) {
2018-08-22 12:14:47 +03:00
return $args['x'] + $args['y'];
2018-09-02 13:44:21 +03:00
},
2018-08-22 12:14:47 +03:00
];
$result = GraphQL::executeQuery(
2017-02-19 22:26:56 +03:00
$schema,
'{ add(x: 34, y: 55) }',
2018-08-22 12:14:47 +03:00
$root
2017-02-19 22:26:56 +03:00
);
2018-09-19 18:12:09 +03:00
self::assertEquals(['data' => ['add' => 89]], $result->toArray(true));
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple Type')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleType() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
type HelloScalars {
str: String
int: Int
2017-02-19 22:26:56 +03:00
float: Float
id: ID
bool: Boolean
2017-02-19 22:26:56 +03:00
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
2018-09-02 13:44:21 +03:00
private function cycleOutput($body, $options = [])
{
$ast = Parser::parse($body);
$schema = BuildSchema::buildAST($ast, null, $options);
return "\n" . SchemaPrinter::doPrint($schema, $options);
}
2017-02-19 22:26:56 +03:00
/**
* @see it('With directives')
2017-02-19 22:26:56 +03:00
*/
public function testWithDirectives() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
directive @foo(arg: Int) on FIELD
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
str: String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Supports descriptions')
2017-02-19 22:26:56 +03:00
*/
public function testSupportsDescriptions() : void
{
2018-09-02 13:44:21 +03:00
$body = '
"""This is a directive"""
directive @foo(
"""It has an argument"""
arg: Int
) on FIELD
"""With an enum"""
enum Color {
RED
"""Not a creative color"""
GREEN
BLUE
}
"""What a great type"""
2018-08-22 12:14:47 +03:00
type Query {
"""And a field to boot"""
str: String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($body, $output);
}
/**
* @see it('Supports option for comment descriptions')
*/
public function testSupportsOptionForCommentDescriptions() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
# This is a directive
directive @foo(
# It has an argument
arg: Int
) on FIELD
# With an enum
enum Color {
RED
# Not a creative color
GREEN
BLUE
}
# What a great type
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
# And a field to boot
str: String
}
';
2018-09-02 13:44:21 +03:00
$output = $this->cycleOutput($body, ['commentDescriptions' => true]);
2018-09-19 18:12:09 +03:00
self::assertEquals($body, $output);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Maintains @skip & @include')
2017-02-19 22:26:56 +03:00
*/
public function testMaintainsSkipAndInclude() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
str: String
}
';
$schema = BuildSchema::buildAST(Parser::parse($body));
2018-09-19 18:12:09 +03:00
self::assertEquals(count($schema->getDirectives()), 3);
self::assertEquals($schema->getDirective('skip'), Directive::skipDirective());
self::assertEquals($schema->getDirective('include'), Directive::includeDirective());
self::assertEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Overriding directives excludes specified')
2017-02-19 22:26:56 +03:00
*/
public function testOverridingDirectivesExcludesSpecified() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
directive @skip on FIELD
directive @include on FIELD
directive @deprecated on FIELD_DEFINITION
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
str: String
}
';
$schema = BuildSchema::buildAST(Parser::parse($body));
2018-09-19 18:12:09 +03:00
self::assertEquals(count($schema->getDirectives()), 3);
self::assertNotEquals($schema->getDirective('skip'), Directive::skipDirective());
self::assertNotEquals($schema->getDirective('include'), Directive::includeDirective());
self::assertNotEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
2017-02-19 22:26:56 +03:00
}
2018-08-22 12:14:47 +03:00
/**
* @see it('Adding directives maintains @skip & @include')
2018-08-22 12:14:47 +03:00
*/
public function testAddingDirectivesMaintainsSkipAndInclude() : void
2018-08-22 12:14:47 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
directive @foo(arg: Int) on FIELD
type Query {
str: String
}
';
$schema = BuildSchema::buildAST(Parser::parse($body));
2018-09-19 18:12:09 +03:00
self::assertCount(4, $schema->getDirectives());
self::assertNotEquals(null, $schema->getDirective('skip'));
self::assertNotEquals(null, $schema->getDirective('include'));
self::assertNotEquals(null, $schema->getDirective('deprecated'));
2018-08-22 12:14:47 +03:00
}
2017-02-19 22:26:56 +03:00
/**
* @see it('Type modifiers')
2017-02-19 22:26:56 +03:00
*/
public function testTypeModifiers() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
type HelloScalars {
nonNullStr: String!
listOfStrs: [String]
listOfNonNullStrs: [String!]
nonNullListOfStrs: [String]!
nonNullListOfNonNullStrs: [String!]!
2017-02-19 22:26:56 +03:00
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Recursive type')
2017-02-19 22:26:56 +03:00
*/
public function testRecursiveType() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query {
str: String
2018-08-22 12:14:47 +03:00
recurse: Query
2017-02-19 22:26:56 +03:00
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Two types circular')
2017-02-19 22:26:56 +03:00
*/
public function testTwoTypesCircular() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
schema {
query: TypeOne
}
type TypeOne {
str: String
typeTwo: TypeTwo
}
type TypeTwo {
str: String
typeOne: TypeOne
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Single argument field')
2017-02-19 22:26:56 +03:00
*/
public function testSingleArgumentField() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query {
str(int: Int): String
2017-02-19 22:26:56 +03:00
floatToStr(float: Float): String
idToStr(id: ID): String
booleanToStr(bool: Boolean): String
2017-02-19 22:26:56 +03:00
strToStr(bool: String): String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple type with multiple arguments')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithMultipleArguments() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
str(int: Int, bool: Boolean): String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple type with interface')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithInterface() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query implements WorldInterface {
2017-02-19 22:26:56 +03:00
str: String
}
interface WorldInterface {
str: String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple output enum')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleOutputEnum() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
enum Hello {
WORLD
}
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
hello: Hello
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple input enum')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleInputEnum() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
enum Hello {
WORLD
}
type Query {
str(hello: Hello): String
2017-02-19 22:26:56 +03:00
}
2018-08-22 12:14:47 +03:00
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($body, $output);
2018-08-22 12:14:47 +03:00
}
2017-02-19 22:26:56 +03:00
2018-08-22 12:14:47 +03:00
/**
* @see it('Multiple value enum')
2018-08-22 12:14:47 +03:00
*/
public function testMultipleValueEnum() : void
2018-08-22 12:14:47 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
enum Hello {
WO
RLD
}
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
hello: Hello
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple Union')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleUnion() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
union Hello = World
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
hello: Hello
}
type World {
str: String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Multiple Union')
2017-02-19 22:26:56 +03:00
*/
public function testMultipleUnion() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
union Hello = WorldOne | WorldTwo
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
hello: Hello
}
type WorldOne {
str: String
}
type WorldTwo {
str: String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Specifying Union type using __typename')
*/
public function testSpecifyingUnionTypeUsingTypename() : void
{
2019-06-30 21:54:56 +03:00
$schema = BuildSchema::buildAST(Parser::parse('
2018-08-22 12:14:47 +03:00
type Query {
fruits: [Fruit]
}
union Fruit = Apple | Banana
type Apple {
color: String
}
type Banana {
length: Int
}
'));
2019-06-30 21:54:56 +03:00
$query = '
{
fruits {
... on Apple {
color
}
... on Banana {
length
}
}
}
';
2019-06-30 21:54:56 +03:00
$rootValue = [
'fruits' => [
[
2018-09-02 13:44:21 +03:00
'color' => 'green',
'__typename' => 'Apple',
],
[
2018-09-02 13:44:21 +03:00
'length' => 5,
'__typename' => 'Banana',
2018-09-02 13:44:21 +03:00
],
],
];
2019-06-30 21:54:56 +03:00
$expected = [
'data' => [
'fruits' => [
['color' => 'green'],
['length' => 5],
2018-09-02 13:44:21 +03:00
],
],
];
2019-06-30 21:54:56 +03:00
$result = GraphQL::executeQuery($schema, $query, $rootValue);
2018-09-19 18:12:09 +03:00
self::assertEquals($expected, $result->toArray(true));
}
/**
* @see it('Specifying Interface type using __typename')
*/
public function testSpecifyingInterfaceUsingTypename() : void
{
2019-06-30 21:54:56 +03:00
$schema = BuildSchema::buildAST(Parser::parse('
2018-08-22 12:14:47 +03:00
type Query {
characters: [Character]
}
interface Character {
name: String!
}
type Human implements Character {
name: String!
totalCredits: Int
}
type Droid implements Character {
name: String!
primaryFunction: String
}
'));
2019-06-30 21:54:56 +03:00
$query = '
{
characters {
name
... on Human {
totalCredits
}
... on Droid {
primaryFunction
}
}
}
';
2019-06-30 21:54:56 +03:00
$rootValue = [
'characters' => [
[
2018-09-02 13:44:21 +03:00
'name' => 'Han Solo',
'totalCredits' => 10,
2018-09-02 13:44:21 +03:00
'__typename' => 'Human',
],
[
2018-09-02 13:44:21 +03:00
'name' => 'R2-D2',
'primaryFunction' => 'Astromech',
2018-09-02 13:44:21 +03:00
'__typename' => 'Droid',
],
],
];
2019-06-30 21:54:56 +03:00
$expected = [
'data' => [
'characters' => [
['name' => 'Han Solo', 'totalCredits' => 10],
['name' => 'R2-D2', 'primaryFunction' => 'Astromech'],
2018-09-02 13:44:21 +03:00
],
],
];
2019-06-30 21:54:56 +03:00
$result = GraphQL::executeQuery($schema, $query, $rootValue);
2018-09-19 18:12:09 +03:00
self::assertEquals($expected, $result->toArray(true));
}
2017-02-19 22:26:56 +03:00
/**
* @see it('Custom Scalar')
2017-02-19 22:26:56 +03:00
*/
public function testCustomScalar() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
scalar CustomScalar
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
customScalar: CustomScalar
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Input Object')
2017-02-19 22:26:56 +03:00
*/
public function testInputObject() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
input Input {
int: Int
}
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
field(in: Input): String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple argument field with default')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleArgumentFieldWithDefault() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query {
2017-02-19 22:26:56 +03:00
str(int: Int = 2): String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Custom scalar argument field with default')
*/
public function testCustomScalarArgumentFieldWithDefault() : void
{
2018-09-02 13:44:21 +03:00
$body = '
scalar CustomScalar
2018-08-22 12:14:47 +03:00
type Query {
str(int: CustomScalar = 2): String
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
}
2017-02-19 22:26:56 +03:00
/**
* @see it('Simple type with mutation')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithMutation() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
schema {
query: HelloScalars
mutation: Mutation
}
type HelloScalars {
str: String
int: Int
bool: Boolean
2017-02-19 22:26:56 +03:00
}
type Mutation {
addHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Simple type with subscription')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithSubscription() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
schema {
query: HelloScalars
subscription: Subscription
}
type HelloScalars {
str: String
int: Int
bool: Boolean
2017-02-19 22:26:56 +03:00
}
type Subscription {
subscribeHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Unreferenced type implementing referenced interface')
2017-02-19 22:26:56 +03:00
*/
public function testUnreferencedTypeImplementingReferencedInterface() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
type Concrete implements Iface {
key: String
}
interface Iface {
key: String
}
type Query {
iface: Iface
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Unreferenced type implementing referenced union')
2017-02-19 22:26:56 +03:00
*/
public function testUnreferencedTypeImplementingReferencedUnion() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
type Concrete {
key: String
}
type Query {
union: Union
}
union Union = Concrete
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Supports @deprecated')
2017-02-19 22:26:56 +03:00
*/
public function testSupportsDeprecated() : void
2017-02-19 22:26:56 +03:00
{
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
enum MyEnum {
VALUE
OLD_VALUE @deprecated
OTHER_VALUE @deprecated(reason: "Terrible reasons")
}
type Query {
field1: String @deprecated
field2: Int @deprecated(reason: "Because I said so")
enum: MyEnum
2017-02-19 22:26:56 +03:00
}
';
$output = $this->cycleOutput($body);
2018-09-19 18:12:09 +03:00
self::assertEquals($output, $body);
2017-02-19 22:26:56 +03:00
2018-09-02 13:44:21 +03:00
$ast = Parser::parse($body);
2017-02-19 22:26:56 +03:00
$schema = BuildSchema::buildAST($ast);
/** @var EnumType $myEnum */
$myEnum = $schema->getType('MyEnum');
$value = $myEnum->getValue('VALUE');
2018-09-19 18:12:09 +03:00
self::assertFalse($value->isDeprecated());
$oldValue = $myEnum->getValue('OLD_VALUE');
2018-09-19 18:12:09 +03:00
self::assertTrue($oldValue->isDeprecated());
self::assertEquals('No longer supported', $oldValue->deprecationReason);
$otherValue = $myEnum->getValue('OTHER_VALUE');
2018-09-19 18:12:09 +03:00
self::assertTrue($otherValue->isDeprecated());
self::assertEquals('Terrible reasons', $otherValue->deprecationReason);
2017-02-19 22:26:56 +03:00
$rootFields = $schema->getType('Query')->getFields();
2018-09-19 18:12:09 +03:00
self::assertEquals($rootFields['field1']->isDeprecated(), true);
self::assertEquals($rootFields['field1']->deprecationReason, 'No longer supported');
2017-02-19 22:26:56 +03:00
2018-09-19 18:12:09 +03:00
self::assertEquals($rootFields['field2']->isDeprecated(), true);
self::assertEquals($rootFields['field2']->deprecationReason, 'Because I said so');
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Correctly assign AST nodes')
*/
public function testCorrectlyAssignASTNodes() : void
{
2018-08-22 12:14:47 +03:00
$schemaAST = Parser::parse('
schema {
query: Query
}
type Query {
testField(testArg: TestInput): TestUnion
}
input TestInput {
testInputField: TestEnum
}
enum TestEnum {
TEST_VALUE
}
union TestUnion = TestType
interface TestInterface {
interfaceField: String
}
type TestType implements TestInterface {
interfaceField: String
}
2018-08-22 12:14:47 +03:00
scalar TestScalar
2018-08-22 12:14:47 +03:00
directive @test(arg: TestScalar) on FIELD
');
2018-09-02 13:44:21 +03:00
$schema = BuildSchema::buildAST($schemaAST);
/** @var ObjectType $query */
2018-09-02 13:44:21 +03:00
$query = $schema->getType('Query');
$testInput = $schema->getType('TestInput');
$testEnum = $schema->getType('TestEnum');
$testUnion = $schema->getType('TestUnion');
$testInterface = $schema->getType('TestInterface');
2018-09-02 13:44:21 +03:00
$testType = $schema->getType('TestType');
$testScalar = $schema->getType('TestScalar');
$testDirective = $schema->getDirective('test');
$restoredIDL = SchemaPrinter::doPrint(BuildSchema::build(
Printer::doPrint($schema->getAstNode()) . "\n" .
Printer::doPrint($query->astNode) . "\n" .
Printer::doPrint($testInput->astNode) . "\n" .
Printer::doPrint($testEnum->astNode) . "\n" .
Printer::doPrint($testUnion->astNode) . "\n" .
Printer::doPrint($testInterface->astNode) . "\n" .
Printer::doPrint($testType->astNode) . "\n" .
2018-08-22 12:14:47 +03:00
Printer::doPrint($testScalar->astNode) . "\n" .
Printer::doPrint($testDirective->astNode)
));
2018-09-19 18:12:09 +03:00
self::assertEquals($restoredIDL, SchemaPrinter::doPrint($schema));
$testField = $query->getField('testField');
2018-09-19 18:12:09 +03:00
self::assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
self::assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
self::assertEquals(
2018-09-02 13:44:21 +03:00
'testInputField: TestEnum',
Printer::doPrint($testInput->getField('testInputField')->astNode)
);
2018-09-19 18:12:09 +03:00
self::assertEquals('TEST_VALUE', Printer::doPrint($testEnum->getValue('TEST_VALUE')->astNode));
self::assertEquals(
2018-09-02 13:44:21 +03:00
'interfaceField: String',
Printer::doPrint($testInterface->getField('interfaceField')->astNode)
);
2018-09-19 18:12:09 +03:00
self::assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode));
self::assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode));
2018-08-22 12:14:47 +03:00
}
/**
* @see it('Root operation types with custom names')
2018-08-22 12:14:47 +03:00
*/
public function testRootOperationTypesWithCustomNames() : void
2018-08-22 12:14:47 +03:00
{
$schema = BuildSchema::build('
schema {
query: SomeQuery
mutation: SomeMutation
subscription: SomeSubscription
}
type SomeQuery { str: String }
type SomeMutation { str: String }
type SomeSubscription { str: String }
');
2018-09-19 18:12:09 +03:00
self::assertEquals('SomeQuery', $schema->getQueryType()->name);
self::assertEquals('SomeMutation', $schema->getMutationType()->name);
self::assertEquals('SomeSubscription', $schema->getSubscriptionType()->name);
2018-08-22 12:14:47 +03:00
}
/**
* @see it('Default root operation type names')
2018-08-22 12:14:47 +03:00
*/
public function testDefaultRootOperationTypeNames() : void
2018-08-22 12:14:47 +03:00
{
$schema = BuildSchema::build('
type Query { str: String }
type Mutation { str: String }
type Subscription { str: String }
');
2018-09-19 18:12:09 +03:00
self::assertEquals('Query', $schema->getQueryType()->name);
self::assertEquals('Mutation', $schema->getMutationType()->name);
self::assertEquals('Subscription', $schema->getSubscriptionType()->name);
2018-08-22 12:14:47 +03:00
}
/**
* @see it('can build invalid schema')
2018-08-22 12:14:47 +03:00
*/
public function testCanBuildInvalidSchema() : void
2018-08-22 12:14:47 +03:00
{
$schema = BuildSchema::build('
# Invalid schema, because it is missing query root type
type Mutation {
str: String
}
');
$errors = $schema->validate();
2018-09-19 18:12:09 +03:00
self::assertGreaterThan(0, $errors);
}
2017-02-19 22:26:56 +03:00
// Describe: Failures
/**
* @see it('Allows only a single schema definition')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleSchemaDefinition() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Must provide only one schema definition.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
}
schema {
query: Hello
}
type Hello {
bar: Bar
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single query type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleQueryType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Must provide only one query type in schema.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
query: Yellow
}
type Hello {
bar: Bar
}
type Yellow {
isColor: Boolean
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single mutation type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleMutationType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Must provide only one mutation type in schema.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
mutation: Hello
mutation: Yellow
}
type Hello {
bar: Bar
}
type Yellow {
isColor: Boolean
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single subscription type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleSubscriptionType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Must provide only one subscription type in schema.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
subscription: Hello
subscription: Yellow
}
type Hello {
bar: Bar
}
type Yellow {
isColor: Boolean
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown type referenced')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownTypeReferenced() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
schema {
query: Hello
}
type Hello {
bar: Bar
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
$schema = BuildSchema::buildAST($doc);
$schema->getTypeMap();
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Unknown type in interface list')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownTypeInInterfaceList() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2018-09-02 13:44:21 +03:00
$body = '
2018-08-22 12:14:47 +03:00
type Query implements Bar {
field: String
}
2017-02-19 22:26:56 +03:00
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
$schema = BuildSchema::buildAST($doc);
$schema->getTypeMap();
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Unknown type in union list')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownTypeInUnionList() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2018-09-02 13:44:21 +03:00
$body = '
2017-02-19 22:26:56 +03:00
union TestUnion = Bar
2018-08-22 12:14:47 +03:00
type Query { testUnion: TestUnion }
2017-02-19 22:26:56 +03:00
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
$schema = BuildSchema::buildAST($doc);
$schema->getTypeMap();
2017-02-19 22:26:56 +03:00
}
/**
* @see it('Unknown query type')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownQueryType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Specified query type "Wat" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Wat
}
type Hello {
str: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown mutation type')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownMutationType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Specified mutation type "Wat" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
mutation: Wat
}
type Hello {
str: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown subscription type')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownSubscriptionType() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Specified subscription type "Awesome" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
mutation: Wat
subscription: Awesome
}
type Hello {
str: String
}
type Wat {
str: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Does not consider operation names')
2017-02-19 22:26:56 +03:00
*/
public function testDoesNotConsiderOperationNames() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Specified query type "Foo" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Foo
}
query Foo { field }
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
/**
* @see it('Does not consider fragment names')
2017-02-19 22:26:56 +03:00
*/
public function testDoesNotConsiderFragmentNames() : void
2017-02-19 22:26:56 +03:00
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Specified query type "Foo" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Foo
}
fragment Foo on Type { field }
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-02-19 22:26:56 +03:00
BuildSchema::buildAST($doc);
}
2017-07-04 09:59:46 +03:00
/**
* @see it('Forbids duplicate type definitions')
2017-07-04 09:59:46 +03:00
*/
public function testForbidsDuplicateTypeDefinitions() : void
2017-07-04 09:59:46 +03:00
{
$body = '
schema {
query: Repeated
}
type Repeated {
id: Int
}
type Repeated {
id: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
2017-07-04 09:59:46 +03:00
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Repeated" was defined more than once.');
2017-07-04 09:59:46 +03:00
BuildSchema::buildAST($doc);
}
public function testSupportsTypeConfigDecorator() : void
{
$body = '
schema {
query: Query
}
type Query {
str: String
color: Color
hello: Hello
}
enum Color {
RED
GREEN
BLUE
}
interface Hello {
world: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
$decorated = [];
2018-09-02 13:44:21 +03:00
$calls = [];
2018-09-26 12:07:23 +03:00
$typeConfigDecorator = static function ($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) {
$decorated[] = $defaultConfig['name'];
2018-09-02 13:44:21 +03:00
$calls[] = [$defaultConfig, $node, $allNodesMap];
return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
};
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
$schema->getTypeMap();
2018-09-19 18:12:09 +03:00
self::assertEquals(['Query', 'Color', 'Hello'], $decorated);
2018-09-26 12:07:23 +03:00
[$defaultConfig, $node, $allNodesMap] = $calls[0];
2018-09-19 18:12:09 +03:00
self::assertInstanceOf(ObjectTypeDefinitionNode::class, $node);
self::assertEquals('Query', $defaultConfig['name']);
2018-09-26 12:07:23 +03:00
self::assertInstanceOf(Closure::class, $defaultConfig['fields']);
self::assertInstanceOf(Closure::class, $defaultConfig['interfaces']);
2018-09-19 18:12:09 +03:00
self::assertArrayHasKey('description', $defaultConfig);
self::assertCount(5, $defaultConfig);
self::assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
self::assertEquals('My description of Query', $schema->getType('Query')->description);
2018-09-26 12:07:23 +03:00
[$defaultConfig, $node, $allNodesMap] = $calls[1];
2018-09-19 18:12:09 +03:00
self::assertInstanceOf(EnumTypeDefinitionNode::class, $node);
self::assertEquals('Color', $defaultConfig['name']);
$enumValue = [
2018-09-02 13:44:21 +03:00
'description' => '',
'deprecationReason' => '',
];
2018-09-19 18:12:09 +03:00
self::assertArraySubset(
2018-09-02 13:44:21 +03:00
[
'RED' => $enumValue,
'GREEN' => $enumValue,
'BLUE' => $enumValue,
],
$defaultConfig['values']
);
2018-09-19 18:12:09 +03:00
self::assertCount(4, $defaultConfig); // 3 + astNode
self::assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
self::assertEquals('My description of Color', $schema->getType('Color')->description);
2018-09-26 12:07:23 +03:00
[$defaultConfig, $node, $allNodesMap] = $calls[2];
2018-09-19 18:12:09 +03:00
self::assertInstanceOf(InterfaceTypeDefinitionNode::class, $node);
self::assertEquals('Hello', $defaultConfig['name']);
2018-09-26 12:07:23 +03:00
self::assertInstanceOf(Closure::class, $defaultConfig['fields']);
2018-09-19 18:12:09 +03:00
self::assertArrayHasKey('description', $defaultConfig);
self::assertCount(4, $defaultConfig);
self::assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
self::assertEquals('My description of Hello', $schema->getType('Hello')->description);
}
public function testCreatesTypesLazily() : void
{
2018-09-02 13:44:21 +03:00
$body = '
schema {
query: Query
}
type Query {
str: String
color: Color
hello: Hello
}
enum Color {
RED
GREEN
BLUE
}
interface Hello {
world: String
}
type World implements Hello {
world: String
}
';
2018-09-02 13:44:21 +03:00
$doc = Parser::parse($body);
$created = [];
2018-09-26 12:07:23 +03:00
$typeConfigDecorator = static function ($config, $node) use (&$created) {
$created[] = $node->name->value;
2018-09-02 13:44:21 +03:00
return $config;
};
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
2018-09-19 18:12:09 +03:00
self::assertEquals(['Query'], $created);
$schema->getType('Color');
2018-09-19 18:12:09 +03:00
self::assertEquals(['Query', 'Color'], $created);
$schema->getType('Hello');
2018-09-19 18:12:09 +03:00
self::assertEquals(['Query', 'Color', 'Hello'], $created);
$types = $schema->getTypeMap();
2018-09-19 18:12:09 +03:00
self::assertEquals(['Query', 'Color', 'Hello', 'World'], $created);
self::assertArrayHasKey('Query', $types);
self::assertArrayHasKey('Color', $types);
self::assertArrayHasKey('Hello', $types);
self::assertArrayHasKey('World', $types);
}
2017-02-19 22:26:56 +03:00
}