From e2875953e1fc0fcaf9f28cb2a5880689dea89362 Mon Sep 17 00:00:00 2001 From: vladar Date: Thu, 19 Jan 2017 19:23:00 +0700 Subject: [PATCH] Tests for new GraphQL\Server facade --- src/Server.php | 134 +++++++++-- tests/ServerTest.php | 544 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 653 insertions(+), 25 deletions(-) diff --git a/src/Server.php b/src/Server.php index 3d99120..e10f15d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -2,11 +2,11 @@ namespace GraphQL; use GraphQL\Error\Error; +use GraphQL\Error\InvariantViolation; use GraphQL\Executor\ExecutionResult; use GraphQL\Executor\Promise\PromiseAdapter; use GraphQL\Language\AST\DocumentNode; use GraphQL\Language\Parser; -use GraphQL\Type\Definition\Config; use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; @@ -17,7 +17,6 @@ class Server { const DEBUG_PHP_ERRORS = 1; const DEBUG_EXCEPTIONS = 2; - const DEBUG_SCHEMA_CONFIGS = 4; const DEBUG_ALL = 7; private $queryType; @@ -88,6 +87,7 @@ class Server */ public function setQueryType(ObjectType $queryType) { + $this->assertSchemaNotSet('Query Type', __METHOD__); $this->queryType = $queryType; return $this; } @@ -106,6 +106,7 @@ class Server */ public function setMutationType(ObjectType $mutationType) { + $this->assertSchemaNotSet('Mutation Type', __METHOD__); $this->mutationType = $mutationType; return $this; } @@ -124,6 +125,7 @@ class Server */ public function setSubscriptionType($subscriptionType) { + $this->assertSchemaNotSet('Subscription Type', __METHOD__); $this->subscriptionType = $subscriptionType; return $this; } @@ -134,10 +136,16 @@ class Server */ public function addTypes(array $types) { - $this->types = array_merge($this->types, $types); + if (!empty($types)) { + $this->assertSchemaNotSet('Types', __METHOD__); + $this->types = array_merge($this->types, $types); + } return $this; } + /** + * @return array + */ public function getTypes() { return $this->types; @@ -161,6 +169,7 @@ class Server */ public function setDirectives(array $directives) { + $this->assertSchemaNotSet('Directives', __METHOD__); $this->directives = $directives; return $this; } @@ -219,6 +228,58 @@ class Server return $this->rootValue; } + /** + * Set schema instance manually. Mutually exclusive with `setQueryType`, `setMutationType`, `setSubscriptionType`, `setDirectives`, `addTypes` + * + * @param Schema $schema + * @return $this + */ + public function setSchema(Schema $schema) + { + if ($this->queryType) { + $err = 'Query Type is already set'; + $errMethod = __CLASS__ . '::setQueryType'; + } else if ($this->mutationType) { + $err = 'Mutation Type is already set'; + $errMethod = __CLASS__ . '::setMutationType'; + } else if ($this->subscriptionType) { + $err = 'Subscription Type is already set'; + $errMethod = __CLASS__ . '::setSubscriptionType'; + } else if ($this->directives) { + $err = 'Directives are already set'; + $errMethod = __CLASS__ . '::setDirectives'; + } else if ($this->types) { + $err = 'Additional types are already set'; + $errMethod = __CLASS__ . '::addTypes'; + } else if ($this->typeResolutionStrategy) { + $err = 'Type Resolution Strategy is already set'; + $errMethod = __CLASS__ . '::setTypeResolutionStrategy'; + } else if ($this->schema && $this->schema !== $schema) { + $err = 'Different schema is already set'; + } + + if (isset($err)) { + if (isset($errMethod)) { + $err .= " ($errMethod is mutually exclusive with " . __METHOD__ . ")"; + } + throw new InvariantViolation("Cannot set Schema on Server: $err"); + } + $this->schema = $schema; + return $this; + } + + /** + * @param $entry + * @param $mutuallyExclusiveMethod + */ + private function assertSchemaNotSet($entry, $mutuallyExclusiveMethod) + { + if ($this->schema) { + $schemaMethod = __CLASS__ . '::setSchema'; + throw new InvariantViolation("Cannot set $entry on Server: Schema is already set ($mutuallyExclusiveMethod is mutually exclusive with $schemaMethod)"); + } + } + /** * @return Schema */ @@ -246,6 +307,8 @@ class Server } /** + * Expects function(\ErrorException $e) : array + * * @param callable $phpErrorFormatter */ public function setPhpErrorFormatter(callable $phpErrorFormatter) @@ -262,6 +325,8 @@ class Server } /** + * Expects function(Exception $e) : array + * * @param callable $exceptionFormatter */ public function setExceptionFormatter(callable $exceptionFormatter) @@ -302,8 +367,11 @@ class Server } /** + * Parses GraphQL query string and returns Abstract Syntax Tree for this query + * * @param string $query * @return Language\AST\DocumentNode + * @throws \GraphQL\Error\SyntaxError */ public function parse($query) { @@ -323,10 +391,12 @@ class Server /** * @param array $validationRules + * @return $this */ public function setValidationRules(array $validationRules) { $this->validationRules = $validationRules; + return $this; } /** @@ -343,12 +413,13 @@ class Server */ public function setTypeResolutionStrategy(Resolution $typeResolutionStrategy) { + $this->assertSchemaNotSet('Type Resolution Strategy', __METHOD__); $this->typeResolutionStrategy = $typeResolutionStrategy; return $this; } /** - * @return PromiseAdapter + * @return PromiseAdapter|null */ public function getPromiseAdapter() { @@ -363,22 +434,33 @@ class Server */ public function setPromiseAdapter(PromiseAdapter $promiseAdapter) { + if ($this->promiseAdapter && $promiseAdapter !== $this->promiseAdapter) { + throw new InvariantViolation("Cannot set promise adapter: Different adapter is already set"); + } $this->promiseAdapter = $promiseAdapter; return $this; } /** - * Returns array with validation errors + * Returns array with validation errors (empty when there are no validation errors) * * @param DocumentNode $query * @return array */ public function validate(DocumentNode $query) { - return DocumentValidator::validate($this->getSchema(), $query, $this->validationRules); + try { + $schema = $this->getSchema(); + } catch (InvariantViolation $e) { + throw new InvariantViolation("Cannot validate, schema contains errors: {$e->getMessage()}", null, $e); + } + + return DocumentValidator::validate($schema, $query, $this->validationRules); } /** + * Executes GraphQL query against this server and returns execution result + * * @param string|DocumentNode $query * @param array|null $variables * @param string|null $operationName @@ -389,17 +471,10 @@ class Server $this->phpErrors = []; if ($this->debug & static::DEBUG_PHP_ERRORS) { // Catch custom errors (to report them in query results) - $lastDisplayErrors = ini_get('display_errors'); - ini_set('display_errors', 0); - set_error_handler(function($severity, $message, $file, $line) { $this->phpErrors[] = new \ErrorException($message, 0, $severity, $file, $line); }); } - if ($this->debug & static::DEBUG_SCHEMA_CONFIGS) { - $isConfigValidationEnabled = Config::isValidationEnabled(); - Config::enableValidation(); - } $result = GraphQL::executeAndReturnResult( $this->getSchema(), @@ -420,38 +495,40 @@ class Server $result->extensions['phpErrors'] = array_map($this->phpErrorFormatter, $this->phpErrors); } - if (isset($lastDisplayErrors)) { - ini_set('display_errors', $lastDisplayErrors); + if ($this->debug & static::DEBUG_PHP_ERRORS) { restore_error_handler(); } - if (isset($isConfigValidationEnabled) && !$isConfigValidationEnabled) { - Config::disableValidation(); - } return $result; } + /** + * GraphQL HTTP endpoint compatible with express-graphql + */ public function handleRequest() { try { $httpStatus = 200; if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) { - $raw = file_get_contents('php://input') ?: ''; + $raw = $this->readInput(); $data = json_decode($raw, true); } else { $data = $_REQUEST; } $data += ['query' => null, 'variables' => null]; $result = $this->executeQuery($data['query'], (array) $data['variables'])->toArray(); - } catch (\Exception $exception) { + } catch (\Exception $e) { // This is only possible for schema creation errors and some very unpredictable errors, // (all errors which occur during query execution are caught and included in final response) $httpStatus = $this->unexpectedErrorStatus; - $error = new Error($this->unexpectedErrorMessage, null, null, null, null, $exception); + $error = new Error($this->unexpectedErrorMessage, null, null, null, null, $e); + $result = ['errors' => [$this->formatError($error)]]; + } catch (\Error $e) { + $httpStatus = $this->unexpectedErrorStatus; + $error = new Error($this->unexpectedErrorMessage, null, null, null, null, $e); $result = ['errors' => [$this->formatError($error)]]; } - header('Content-Type: application/json', true, $httpStatus); - echo json_encode($result); + $this->produceOutput($result, $httpStatus); } private function formatException(\Exception $e) @@ -473,4 +550,15 @@ class Server } return $result; } + + protected function readInput() + { + return file_get_contents('php://input') ?: ''; + } + + protected function produceOutput(array $result, $httpStatus) + { + header('Content-Type: application/json', true, $httpStatus); + echo json_encode($result); + } } diff --git a/tests/ServerTest.php b/tests/ServerTest.php index d1e7ebb..f249eb0 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -1,9 +1,17 @@ assertEquals(null, $server->getQueryType()); $this->assertEquals(null, $server->getMutationType()); $this->assertEquals(null, $server->getSubscriptionType()); + $this->assertEquals(Directive::getInternalDirectives(), $server->getDirectives()); + $this->assertEquals([], $server->getTypes()); + $this->assertEquals(null, $server->getTypeResolutionStrategy()); + $this->assertEquals(null, $server->getContext()); $this->assertEquals(null, $server->getRootValue()); $this->assertEquals(0, $server->getDebug()); - $this->assertEquals(Directive::getInternalDirectives(), $server->getDirectives()); + $this->assertEquals(['GraphQL\Error\FormattedError', 'createFromException'], $server->getExceptionFormatter()); $this->assertEquals(['GraphQL\Error\FormattedError', 'createFromPHPError'], $server->getPhpErrorFormatter()); $this->assertEquals(null, $server->getPromiseAdapter()); - $this->assertEquals(null, $server->getTypeResolutionStrategy()); $this->assertEquals('Unexpected Error', $server->getUnexpectedErrorMessage()); $this->assertEquals(500, $server->getUnexpectedErrorStatus()); $this->assertEquals(DocumentValidator::allRules(), $server->getValidationRules()); @@ -33,4 +44,533 @@ class ServerTest extends \PHPUnit_Framework_TestCase $this->assertEquals('Schema query must be Object Type but got: NULL', $e->getMessage()); } } + + public function testSchemaDefinition() + { + $mutationType = $queryType = $subscriptionType = new ObjectType(['name' => 'A', 'fields' => []]); + + $schema = new Schema([ + 'query' => $queryType + ]); + + try { + Server::create() + ->setQueryType($queryType) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Query Type is already set '. + '(GraphQL\Server::setQueryType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setMutationType($mutationType) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Mutation Type is already set '. + '(GraphQL\Server::setMutationType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSubscriptionType($subscriptionType) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Subscription Type is already set '. + '(GraphQL\Server::setSubscriptionType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setDirectives(Directive::getInternalDirectives()) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Directives are already set '. + '(GraphQL\Server::setDirectives is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->addTypes([$queryType, $mutationType]) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Additional types are already set '. + '(GraphQL\Server::addTypes is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + + try { + Server::create() + ->setTypeResolutionStrategy(new EagerResolution([$queryType, $mutationType])) + ->setSchema($schema); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Type Resolution Strategy is already set '. + '(GraphQL\Server::setTypeResolutionStrategy is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->setQueryType($queryType); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Query Type on Server: Schema is already set '. + '(GraphQL\Server::setQueryType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->setMutationType($mutationType); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Mutation Type on Server: Schema is already set '. + '(GraphQL\Server::setMutationType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->setSubscriptionType($subscriptionType); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Subscription Type on Server: Schema is already set '. + '(GraphQL\Server::setSubscriptionType is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->setDirectives([]); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Directives on Server: Schema is already set '. + '(GraphQL\Server::setDirectives is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->addTypes([$queryType, $mutationType]); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Types on Server: Schema is already set '. + '(GraphQL\Server::addTypes is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + // But empty types should work (as they don't change anything): + Server::create() + ->setSchema($schema) + ->addTypes([]); + + try { + Server::create() + ->setSchema($schema) + ->setTypeResolutionStrategy(new EagerResolution([$queryType, $mutationType])); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Type Resolution Strategy on Server: Schema is already set '. + '(GraphQL\Server::setTypeResolutionStrategy is mutually exclusive with GraphQL\Server::setSchema)', + $e->getMessage() + ); + } + + try { + Server::create() + ->setSchema($schema) + ->setSchema(new Schema(['query' => $queryType])); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set Schema on Server: Different schema is already set', + $e->getMessage() + ); + } + + + // This should not throw: + $server = Server::create() + ->setSchema($schema); + + $this->assertSame($schema, $server->getSchema()); + + $server = Server::create() + ->setQueryType($queryType); + $this->assertSame($queryType, $server->getQueryType()); + $this->assertSame($queryType, $server->getSchema()->getQueryType()); + + $server = Server::create() + ->setQueryType($queryType) + ->setMutationType($mutationType); + + $this->assertSame($mutationType, $server->getMutationType()); + $this->assertSame($mutationType, $server->getSchema()->getMutationType()); + + $server = Server::create() + ->setQueryType($queryType) + ->setSubscriptionType($subscriptionType); + + $this->assertSame($subscriptionType, $server->getSubscriptionType()); + $this->assertSame($subscriptionType, $server->getSchema()->getSubscriptionType()); + + $server = Server::create() + ->setQueryType($queryType) + ->addTypes($types = [$queryType, $subscriptionType]); + + $this->assertSame($types, $server->getTypes()); + $server->addTypes([$mutationType]); + $this->assertSame(array_merge($types, [$mutationType]), $server->getTypes()); + + $server = Server::create() + ->setDirectives($directives = []); + + $this->assertSame($directives, $server->getDirectives()); + } + + public function testParse() + { + $server = Server::create(); + $ast = $server->parse('{q}'); + $this->assertInstanceOf('GraphQL\Language\AST\DocumentNode', $ast); + + try { + $server->parse('{q'); + $this->fail('Expected exception not thrown'); + } catch (\GraphQL\Error\SyntaxError $e) { + $this->assertContains('{q', $e->getMessage()); + } + } + + public function testValidate() + { + $server = Server::create() + ->setQueryType(new ObjectType(['name' => 'Q', 'fields' => []])); + + $ast = $server->parse('{q}'); + $errors = $server->validate($ast); + + $this->assertInternalType('array', $errors); + $this->assertNotEmpty($errors); + + try { + $server = Server::create(); + $server->validate($ast); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot validate, schema contains errors: Schema query must be Object Type but got: NULL', + $e->getMessage() + ); + } + } + + public function testPromiseAdapter() + { + $adapter1 = new SyncPromiseAdapter(); + $adapter2 = new SyncPromiseAdapter(); + + $server = Server::create() + ->setPromiseAdapter($adapter1); + + $this->assertSame($adapter1, $server->getPromiseAdapter()); + $server->setPromiseAdapter($adapter1); + + try { + $server->setPromiseAdapter($adapter2); + $this->fail('Expected exception not thrown'); + } catch (InvariantViolation $e) { + $this->assertEquals( + 'Cannot set promise adapter: Different adapter is already set', + $e->getMessage() + ); + } + } + + public function testValidationRules() + { + $rules = []; + $server = Server::create() + ->setValidationRules($rules); + + $this->assertSame($rules, $server->getValidationRules()); + } + + public function testExecuteQuery() + { + $called = false; + $queryType = new ObjectType([ + 'name' => 'Q', + 'fields' => [ + 'field' => [ + 'type' => Type::string(), + 'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$called) { + $called = true; + $this->assertEquals(null, $context); + $this->assertEquals(null, $value); + $this->assertEquals(null, $info->rootValue); + return 'ok'; + } + ] + ] + ]); + + $server = Server::create() + ->setQueryType($queryType); + + $result = $server->executeQuery('{field}'); + $this->assertEquals(true, $called); + $this->assertInstanceOf('GraphQL\Executor\ExecutionResult', $result); + $this->assertEquals(['data' => ['field' => 'ok']], $result->toArray()); + + $called = false; + $contextValue = new \stdClass(); + $rootValue = new \stdClass(); + + $queryType = new ObjectType([ + 'name' => 'QueryType', + 'fields' => [ + 'field' => [ + 'type' => Type::string(), + 'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$called, $contextValue, $rootValue) { + $called = true; + $this->assertSame($rootValue, $value); + $this->assertSame($contextValue, $context); + $this->assertEquals($rootValue, $info->rootValue); + return 'ok'; + } + ] + ] + ]); + + $server = Server::create() + ->setQueryType($queryType) + ->setRootValue($rootValue) + ->setContext($contextValue); + + $result = $server->executeQuery('{field}'); + $this->assertEquals(true, $called); + $this->assertInstanceOf('GraphQL\Executor\ExecutionResult', $result); + $this->assertEquals(['data' => ['field' => 'ok']], $result->toArray()); + } + + public function testDebugPhpErrors() + { + $queryType = new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'err' => [ + 'type' => Type::string(), + 'resolve' => function() { + trigger_error('notice', E_USER_NOTICE); + return 'err'; + } + ] + ] + ]); + + $server = Server::create() + ->setDebug(0) + ->setQueryType($queryType); + + $prevEnabled = \PHPUnit_Framework_Error_Notice::$enabled; + \PHPUnit_Framework_Error_Notice::$enabled = false; + $result = @$server->executeQuery('{err}'); + + $expected = [ + 'data' => ['err' => 'err'] + ]; + $this->assertEquals($expected, $result->toArray()); + + $server->setDebug(Server::DEBUG_PHP_ERRORS); + $result = @$server->executeQuery('{err}'); + + $expected = [ + 'data' => ['err' => 'err'], + 'extensions' => [ + 'phpErrors' => [ + [ + 'message' => 'notice', + 'severity' => 1024, + // 'trace' => [...] + ] + ] + ] + ]; + + $this->assertArraySubset($expected, $result->toArray()); + + $server->setPhpErrorFormatter(function(\ErrorException $e) { + return ['test' => $e->getMessage()]; + }); + + $result = $server->executeQuery('{err}'); + $expected = [ + 'data' => ['err' => 'err'], + 'extensions' => [ + 'phpErrors' => [ + [ + 'test' => 'notice' + ] + ] + ] + ]; + $this->assertEquals($expected, $result->toArray()); + + \PHPUnit_Framework_Error_Notice::$enabled = $prevEnabled; + } + + public function testDebugExceptions() + { + $queryType = new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'withException' => [ + 'type' => Type::string(), + 'resolve' => function() { + throw new \Exception("Error"); + } + ] + ] + ]); + + $server = Server::create() + ->setDebug(0) + ->setQueryType($queryType); + + $result = $server->executeQuery('{withException}'); + $expected = [ + 'data' => [ + 'withException' => null + ], + 'errors' => [[ + 'message' => 'Error', + 'path' => ['withException'], + 'locations' => [[ + 'line' => 1, + 'column' => 2 + ]] + ]] + ]; + $this->assertEquals($expected, $result->toArray()); + + $server->setDebug(Server::DEBUG_EXCEPTIONS); + $result = $server->executeQuery('{withException}'); + + $expected['errors'][0]['exception'] = ['message' => 'Error', 'trace' => []]; + $this->assertArraySubset($expected, $result->toArray()); + + $server->setExceptionFormatter(function(\Exception $e) { + return ['test' => $e->getMessage()]; + }); + + $result = $server->executeQuery('{withException}'); + $expected['errors'][0]['exception'] = ['test' => 'Error']; + $this->assertEquals($expected, $result->toArray()); + } + + public function testHandleRequest() + { + $mock = $this->getMock('GraphQL\Server', ['readInput', 'produceOutput']); + $mock->method('readInput') + ->will($this->returnValue(json_encode(['query' => '{err}']))); + + $output = null; + $mock->method('produceOutput') + ->will($this->returnCallback(function($a1, $a2) use (&$output) { + $output = func_get_args(); + })); + + /** @var $mock Server */ + $mock->handleRequest(); + + $this->assertInternalType('array', $output); + $this->assertEquals(['errors' => [['message' => 'Unexpected Error']]], $output[0]); + $this->assertEquals(500, $output[1]); + + $output = null; + $mock->setUnexpectedErrorMessage($newErr = 'Hey! Something went wrong!'); + $mock->setUnexpectedErrorStatus(501); + $mock->handleRequest(); + + $this->assertInternalType('array', $output); + $this->assertEquals(['errors' => [['message' => $newErr]]], $output[0]); + $this->assertEquals(501, $output[1]); + + $mock->setQueryType(new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'test' => [ + 'type' => Type::string(), + 'resolve' => function() { + return 'ok'; + } + ] + ] + ])); + + $_REQUEST = ['query' => '{err}']; + $output = null; + $mock->handleRequest(); + $this->assertInternalType('array', $output); + + $expectedOutput = [ + ['errors' => [[ + 'message' => 'Cannot query field "err" on type "Query".', + 'locations' => [[ + 'line' => 1, + 'column' => 2 + ]] + ]]], + 200 + ]; + + $this->assertEquals($expectedOutput, $output); + + $output = null; + $_SERVER['CONTENT_TYPE'] = 'application/json'; + $_REQUEST = []; + $mock->handleRequest(); + + $this->assertInternalType('array', $output); + $this->assertEquals($expectedOutput, $output); + } }