graphql-php/tests/Utils/BuildSchemaTest.php

1287 lines
30 KiB
PHP
Raw Normal View History

2017-02-19 22:26:56 +03:00
<?php
namespace GraphQL\Tests\Utils;
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;
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;
use GraphQL\Type\Definition\Directive;
2018-07-29 18:43:10 +03:00
use PHPUnit\Framework\TestCase;
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
private function cycleOutput($body, $options = [])
2017-02-19 22:26:56 +03:00
{
$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('can use built schema for limited execution')
2017-02-19 22:26:56 +03:00
*/
public function testUseBuiltSchemaForLimitedExecution()
{
$schema = BuildSchema::buildAST(Parser::parse('
type Query {
str: String
}
'));
$result = GraphQL::executeQuery($schema, '{ str }', ['str' => 123]);
$this->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()
{
$schema = BuildSchema::build("
type Query {
add(x: Int, y: Int): Int
}
");
2018-08-22 12:14:47 +03:00
$root = [
'add' => function ($root, $args) {
return $args['x'] + $args['y'];
}
];
$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
);
$this->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()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('With directives')
2017-02-19 22:26:56 +03:00
*/
public function testWithDirectives()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Supports descriptions')
2017-02-19 22:26:56 +03:00
*/
public function testSupportsDescriptions()
{
$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);
$this->assertEquals($body, $output);
}
/**
* @see it('Supports option for comment descriptions')
*/
public function testSupportsOptionForCommentDescriptions()
2017-02-19 22:26:56 +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 {
2017-02-19 22:26:56 +03:00
# And a field to boot
str: String
}
';
$output = $this->cycleOutput($body, [ 'commentDescriptions' => true ]);
$this->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()
{
$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));
$this->assertEquals(count($schema->getDirectives()), 3);
$this->assertEquals($schema->getDirective('skip'), Directive::skipDirective());
$this->assertEquals($schema->getDirective('include'), Directive::includeDirective());
$this->assertEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
}
/**
* @see it('Overriding directives excludes specified')
2017-02-19 22:26:56 +03:00
*/
public function testOverridingDirectivesExcludesSpecified()
{
$body = '
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));
$this->assertEquals(count($schema->getDirectives()), 3);
$this->assertNotEquals($schema->getDirective('skip'), Directive::skipDirective());
$this->assertNotEquals($schema->getDirective('include'), Directive::includeDirective());
$this->assertNotEquals($schema->getDirective('deprecated'), Directive::deprecatedDirective());
}
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()
{
$body = '
directive @foo(arg: Int) on FIELD
type Query {
str: String
}
';
$schema = BuildSchema::buildAST(Parser::parse($body));
$this->assertCount(4, $schema->getDirectives());
$this->assertNotEquals(null, $schema->getDirective('skip'));
$this->assertNotEquals(null, $schema->getDirective('include'));
$this->assertNotEquals(null, $schema->getDirective('deprecated'));
}
2017-02-19 22:26:56 +03:00
/**
* @see it('Type modifiers')
2017-02-19 22:26:56 +03:00
*/
public function testTypeModifiers()
{
$body = '
type HelloScalars {
nonNullStr: String!
listOfStrs: [String]
listOfNonNullStrs: [String!]
nonNullListOfStrs: [String]!
nonNullListOfNonNullStrs: [String!]!
2017-02-19 22:26:56 +03:00
}
';
$output = $this->cycleOutput($body);
$this->assertEquals($output, $body);
}
/**
* @see it('Recursive type')
2017-02-19 22:26:56 +03:00
*/
public function testRecursiveType()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Two types circular')
2017-02-19 22:26:56 +03:00
*/
public function testTwoTypesCircular()
{
$body = '
schema {
query: TypeOne
}
type TypeOne {
str: String
typeTwo: TypeTwo
}
type TypeTwo {
str: String
typeOne: TypeOne
}
';
$output = $this->cycleOutput($body);
$this->assertEquals($output, $body);
}
/**
* @see it('Single argument field')
2017-02-19 22:26:56 +03:00
*/
public function testSingleArgumentField()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple type with multiple arguments')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithMultipleArguments()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple type with interface')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithInterface()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple output enum')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleOutputEnum()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple input enum')
2017-02-19 22:26:56 +03:00
*/
2018-08-22 12:14:47 +03:00
public function testSimpleInputEnum()
2017-02-19 22:26:56 +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);
$this->assertEquals($body, $output);
}
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()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple Union')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleUnion()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Multiple Union')
2017-02-19 22:26:56 +03:00
*/
public function testMultipleUnion()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Specifying Union type using __typename')
*/
public function testSpecifyingUnionTypeUsingTypename()
{
$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
}
'));
$query = '
{
fruits {
... on Apple {
color
}
... on Banana {
length
}
}
}
';
$root = [
'fruits' => [
[
'color' => 'green',
'__typename' => 'Apple',
],
[
'length' => 5,
'__typename' => 'Banana',
]
]
];
$expected = [
'data' => [
'fruits' => [
['color' => 'green'],
['length' => 5],
]
]
];
$result = GraphQL::executeQuery($schema, $query, $root);
$this->assertEquals($expected, $result->toArray(true));
}
/**
* @see it('Specifying Interface type using __typename')
*/
public function testSpecifyingInterfaceUsingTypename()
{
$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
}
'));
$query = '
{
characters {
name
... on Human {
totalCredits
}
... on Droid {
primaryFunction
}
}
}
';
$root = [
'characters' => [
[
'name' => 'Han Solo',
'totalCredits' => 10,
'__typename' => 'Human',
],
[
'name' => 'R2-D2',
'primaryFunction' => 'Astromech',
'__typename' => 'Droid',
]
]
];
$expected = [
'data' => [
'characters' => [
['name' => 'Han Solo', 'totalCredits' => 10],
['name' => 'R2-D2', 'primaryFunction' => 'Astromech'],
]
]
];
$result = GraphQL::executeQuery($schema, $query, $root);
$this->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()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Input Object')
2017-02-19 22:26:56 +03:00
*/
public function testInputObject()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple argument field with default')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleArgumentFieldWithDefault()
{
$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);
$this->assertEquals($output, $body);
}
/**
* @see it('Custom scalar argument field with default')
*/
public function testCustomScalarArgumentFieldWithDefault()
{
$body = '
scalar CustomScalar
2018-08-22 12:14:47 +03:00
type Query {
str(int: CustomScalar = 2): String
}
';
$output = $this->cycleOutput($body);
$this->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()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Simple type with subscription')
2017-02-19 22:26:56 +03:00
*/
public function testSimpleTypeWithSubscription()
{
$body = '
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);
$this->assertEquals($output, $body);
}
/**
* @see it('Unreferenced type implementing referenced interface')
2017-02-19 22:26:56 +03:00
*/
public function testUnreferencedTypeImplementingReferencedInterface()
{
$body = '
type Concrete implements Iface {
key: String
}
interface Iface {
key: String
}
type Query {
iface: Iface
}
';
$output = $this->cycleOutput($body);
$this->assertEquals($output, $body);
}
/**
* @see it('Unreferenced type implementing referenced union')
2017-02-19 22:26:56 +03:00
*/
public function testUnreferencedTypeImplementingReferencedUnion()
{
$body = '
type Concrete {
key: String
}
type Query {
union: Union
}
union Union = Concrete
';
$output = $this->cycleOutput($body);
$this->assertEquals($output, $body);
}
/**
* @see it('Supports @deprecated')
2017-02-19 22:26:56 +03:00
*/
public function testSupportsDeprecated()
{
$body = '
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);
$this->assertEquals($output, $body);
$ast = Parser::parse($body);
$schema = BuildSchema::buildAST($ast);
/** @var EnumType $myEnum */
$myEnum = $schema->getType('MyEnum');
$value = $myEnum->getValue('VALUE');
$this->assertFalse($value->isDeprecated());
$oldValue = $myEnum->getValue('OLD_VALUE');
$this->assertTrue($oldValue->isDeprecated());
$this->assertEquals('No longer supported', $oldValue->deprecationReason);
$otherValue = $myEnum->getValue('OTHER_VALUE');
$this->assertTrue($otherValue->isDeprecated());
$this->assertEquals('Terrible reasons', $otherValue->deprecationReason);
2017-02-19 22:26:56 +03:00
$rootFields = $schema->getType('Query')->getFields();
$this->assertEquals($rootFields['field1']->isDeprecated(), true);
$this->assertEquals($rootFields['field1']->deprecationReason, 'No longer supported');
$this->assertEquals($rootFields['field2']->isDeprecated(), true);
$this->assertEquals($rootFields['field2']->deprecationReason, 'Because I said so');
}
/**
* @see it('Correctly assign AST nodes')
*/
public function testCorrectlyAssignASTNodes()
{
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-08-22 12:14:47 +03:00
$schema = BuildSchema::buildAST($schemaAST);
/** @var ObjectType $query */
$query = $schema->getType('Query');
$testInput = $schema->getType('TestInput');
$testEnum = $schema->getType('TestEnum');
$testUnion = $schema->getType('TestUnion');
$testInterface = $schema->getType('TestInterface');
$testType = $schema->getType('TestType');
2018-08-22 12:14:47 +03:00
$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)
));
$this->assertEquals($restoredIDL, SchemaPrinter::doPrint($schema));
$testField = $query->getField('testField');
$this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
$this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
$this->assertEquals('testInputField: TestEnum', Printer::doPrint($testInput->getField('testInputField')->astNode));
$this->assertEquals('TEST_VALUE', Printer::doPrint($testEnum->getValue('TEST_VALUE')->astNode));
$this->assertEquals('interfaceField: String', Printer::doPrint($testInterface->getField('interfaceField')->astNode));
$this->assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode));
2018-08-22 12:14:47 +03:00
$this->assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode));
}
/**
* @see it('Root operation types with custom names')
2018-08-22 12:14:47 +03:00
*/
public function testRootOperationTypesWithCustomNames()
{
$schema = BuildSchema::build('
schema {
query: SomeQuery
mutation: SomeMutation
subscription: SomeSubscription
}
type SomeQuery { str: String }
type SomeMutation { str: String }
type SomeSubscription { str: String }
');
$this->assertEquals('SomeQuery', $schema->getQueryType()->name);
$this->assertEquals('SomeMutation', $schema->getMutationType()->name);
$this->assertEquals('SomeSubscription', $schema->getSubscriptionType()->name);
}
/**
* @see it('Default root operation type names')
2018-08-22 12:14:47 +03:00
*/
public function testDefaultRootOperationTypeNames()
{
$schema = BuildSchema::build('
type Query { str: String }
type Mutation { str: String }
type Subscription { str: String }
');
$this->assertEquals('Query', $schema->getQueryType()->name);
$this->assertEquals('Mutation', $schema->getMutationType()->name);
$this->assertEquals('Subscription', $schema->getSubscriptionType()->name);
}
/**
* @see it('can build invalid schema')
2018-08-22 12:14:47 +03:00
*/
public function testCanBuildInvalidSchema()
{
$schema = BuildSchema::build('
# Invalid schema, because it is missing query root type
type Mutation {
str: String
}
');
$errors = $schema->validate();
$this->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()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single query type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleQueryType()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single mutation type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleMutationType()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Allows only a single subscription type')
2017-02-19 22:26:56 +03:00
*/
public function testAllowsOnlySingleSubscriptionType()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown type referenced')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownTypeReferenced()
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
schema {
query: Hello
}
type Hello {
bar: Bar
}
';
$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()
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2017-02-19 22:26:56 +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
';
$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()
{
2018-07-29 18:43:10 +03:00
$this->expectException(Error::class);
$this->expectExceptionMessage('Type "Bar" not found in document.');
2017-02-19 22:26:56 +03:00
$body = '
union TestUnion = Bar
2018-08-22 12:14:47 +03:00
type Query { testUnion: TestUnion }
2017-02-19 22:26:56 +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()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown mutation type')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownMutationType()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Unknown subscription type')
2017-02-19 22:26:56 +03:00
*/
public function testUnknownSubscriptionType()
{
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
}
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Does not consider operation names')
2017-02-19 22:26:56 +03:00
*/
public function testDoesNotConsiderOperationNames()
{
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 }
';
$doc = Parser::parse($body);
BuildSchema::buildAST($doc);
}
/**
* @see it('Does not consider fragment names')
2017-02-19 22:26:56 +03:00
*/
public function testDoesNotConsiderFragmentNames()
{
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 }
';
$doc = Parser::parse($body);
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()
{
$body = '
schema {
query: Repeated
}
type Repeated {
id: Int
}
type Repeated {
id: String
}
';
$doc = Parser::parse($body);
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()
{
$body = '
schema {
query: Query
}
type Query {
str: String
color: Color
hello: Hello
}
enum Color {
RED
GREEN
BLUE
}
interface Hello {
world: String
}
';
$doc = Parser::parse($body);
$decorated = [];
$calls = [];
$typeConfigDecorator = function($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) {
$decorated[] = $defaultConfig['name'];
$calls[] = [$defaultConfig, $node, $allNodesMap];
return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
};
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
$schema->getTypeMap();
$this->assertEquals(['Query', 'Color', 'Hello'], $decorated);
list($defaultConfig, $node, $allNodesMap) = $calls[0];
$this->assertInstanceOf(ObjectTypeDefinitionNode::class, $node);
$this->assertEquals('Query', $defaultConfig['name']);
$this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
$this->assertInstanceOf(\Closure::class, $defaultConfig['interfaces']);
$this->assertArrayHasKey('description', $defaultConfig);
$this->assertCount(5, $defaultConfig);
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
$this->assertEquals('My description of Query', $schema->getType('Query')->description);
list($defaultConfig, $node, $allNodesMap) = $calls[1];
$this->assertInstanceOf(EnumTypeDefinitionNode::class, $node);
$this->assertEquals('Color', $defaultConfig['name']);
$enumValue = [
'description' => '',
'deprecationReason' => ''
];
$this->assertArraySubset([
'RED' => $enumValue,
'GREEN' => $enumValue,
'BLUE' => $enumValue,
], $defaultConfig['values']);
$this->assertCount(4, $defaultConfig); // 3 + astNode
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
$this->assertEquals('My description of Color', $schema->getType('Color')->description);
list($defaultConfig, $node, $allNodesMap) = $calls[2];
$this->assertInstanceOf(InterfaceTypeDefinitionNode::class, $node);
$this->assertEquals('Hello', $defaultConfig['name']);
$this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
$this->assertArrayHasKey('description', $defaultConfig);
$this->assertCount(4, $defaultConfig);
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
$this->assertEquals('My description of Hello', $schema->getType('Hello')->description);
}
public function testCreatesTypesLazily()
{
$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
}
';
$doc = Parser::parse($body);
$created = [];
$typeConfigDecorator = function($config, $node) use (&$created) {
$created[] = $node->name->value;
return $config;
};
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
$this->assertEquals(['Query'], $created);
$schema->getType('Color');
$this->assertEquals(['Query', 'Color'], $created);
$schema->getType('Hello');
$this->assertEquals(['Query', 'Color', 'Hello'], $created);
$types = $schema->getTypeMap();
$this->assertEquals(['Query', 'Color', 'Hello', 'World'], $created);
$this->assertArrayHasKey('Query', $types);
$this->assertArrayHasKey('Color', $types);
$this->assertArrayHasKey('Hello', $types);
$this->assertArrayHasKey('World', $types);
}
2017-02-19 22:26:56 +03:00
}