mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
e7513e356a
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
|
.phpcs-cache
|
||||||
composer.phar
|
composer.phar
|
||||||
composer.lock
|
composer.lock
|
||||||
phpcs.xml
|
phpcs.xml
|
||||||
|
phpstan.neon
|
||||||
vendor/
|
vendor/
|
||||||
|
50
.travis.yml
50
.travis.yml
@ -2,26 +2,19 @@ dist: trusty
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 7.1
|
- 7.1
|
||||||
- 7.2
|
- 7.2
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- php: nightly
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.composer/cache
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
|
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
|
||||||
- travis_retry composer self-update
|
- travis_retry composer self-update
|
||||||
|
|
||||||
install:
|
install: travis_retry composer update --prefer-dist
|
||||||
- composer require react/promise:2.*
|
|
||||||
- composer require psr/http-message:1.*
|
|
||||||
- travis_retry composer update --prefer-dist
|
|
||||||
|
|
||||||
script: ./vendor/bin/phpunit --group default,ReactPromise
|
script: ./vendor/bin/phpunit --group default,ReactPromise
|
||||||
|
|
||||||
@ -45,21 +38,16 @@ jobs:
|
|||||||
after_script:
|
after_script:
|
||||||
- wget https://scrutinizer-ci.com/ocular.phar
|
- wget https://scrutinizer-ci.com/ocular.phar
|
||||||
- php ocular.phar code-coverage:upload --format=php-clover clover.xml
|
- php ocular.phar code-coverage:upload --format=php-clover clover.xml
|
||||||
- stage: Pull request coding standard
|
|
||||||
if: type = pull_request
|
- stage: Code Quality
|
||||||
|
php: 7.1
|
||||||
|
env: CODING_STANDARD
|
||||||
install: travis_retry composer install --prefer-dist
|
install: travis_retry composer install --prefer-dist
|
||||||
script:
|
script:
|
||||||
- |
|
- ./vendor/bin/phpcs
|
||||||
if [ $TRAVIS_BRANCH != "master" ]; then
|
|
||||||
git remote set-branches --add origin $TRAVIS_BRANCH;
|
- stage: Code Quality
|
||||||
git fetch origin $TRAVIS_BRANCH;
|
php: 7.1
|
||||||
fi
|
env: STATIC_ANALYSIS
|
||||||
- git merge-base origin/$TRAVIS_BRANCH $TRAVIS_PULL_REQUEST_SHA || git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge --unshallow
|
install: travis_retry composer install --prefer-dist
|
||||||
- wget https://github.com/diff-sniffer/git/releases/download/0.1.0/git-phpcs.phar
|
script: composer static-analysis
|
||||||
- php git-phpcs.phar origin/$TRAVIS_BRANCH...$TRAVIS_PULL_REQUEST_SHA
|
|
||||||
# - stage: Coding standard
|
|
||||||
# if: NOT type = pull_request
|
|
||||||
# php: 7.1
|
|
||||||
# install: travis_retry composer install --prefer-dist
|
|
||||||
# script:
|
|
||||||
# - ./vendor/bin/phpcs
|
|
||||||
|
@ -25,6 +25,8 @@ composer install
|
|||||||
./vendor/bin/phpunit
|
./vendor/bin/phpunit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Some tests have annotation `@see it('<description>')`. It is used for reference to same tests in [graphql-js implementation](https://github.com/graphql/graphql-js) with the same description.
|
||||||
|
|
||||||
## Coding Standard
|
## Coding Standard
|
||||||
Coding standard of this project is based on [Doctrine CS](https://github.com/doctrine/coding-standard). To run the inspection:
|
Coding standard of this project is based on [Doctrine CS](https://github.com/doctrine/coding-standard). To run the inspection:
|
||||||
|
|
||||||
|
@ -15,8 +15,11 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^4.0",
|
"doctrine/coding-standard": "^4.0",
|
||||||
|
"phpstan/phpstan": "^0.10.3",
|
||||||
|
"phpstan/phpstan-phpunit": "^0.10.0",
|
||||||
"phpunit/phpunit": "^7.2",
|
"phpunit/phpunit": "^7.2",
|
||||||
"psr/http-message": "^1.0"
|
"psr/http-message": "^1.0",
|
||||||
|
"react/promise": "2.*"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"preferred-install": "dist",
|
"preferred-install": "dist",
|
||||||
@ -39,6 +42,7 @@
|
|||||||
"psr/http-message": "To use standard GraphQL server"
|
"psr/http-message": "To use standard GraphQL server"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint" : "phpcs"
|
"lint" : "phpcs",
|
||||||
|
"static-analysis": "phpstan analyse --ansi -l 1 -c phpstan.neon.dist src tests"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ first learn about GraphQL on [the official website](http://graphql.org/learn/).
|
|||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Using [composer](https://getcomposer.org/doc/00-intro.md), simply run:
|
Using [composer](https://getcomposer.org/doc/00-intro.md), run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
composer require webonyx/graphql-php
|
composer require webonyx/graphql-php
|
||||||
|
3
phpstan.neon.dist
Normal file
3
phpstan.neon.dist
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
includes:
|
||||||
|
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||||
|
- vendor/phpstan/phpstan-phpunit/rules.neon
|
@ -1,23 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL;
|
namespace GraphQL;
|
||||||
|
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
||||||
|
|
||||||
class Deferred
|
class Deferred
|
||||||
{
|
{
|
||||||
/**
|
/** @var \SplQueue */
|
||||||
* @var \SplQueue
|
|
||||||
*/
|
|
||||||
private static $queue;
|
private static $queue;
|
||||||
|
|
||||||
/**
|
/** @var callable */
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
private $callback;
|
private $callback;
|
||||||
|
|
||||||
/**
|
/** @var SyncPromise */
|
||||||
* @var SyncPromise
|
|
||||||
*/
|
|
||||||
public $promise;
|
public $promise;
|
||||||
|
|
||||||
public static function getQueue()
|
public static function getQueue()
|
||||||
@ -28,7 +25,7 @@ class Deferred
|
|||||||
public static function runQueue()
|
public static function runQueue()
|
||||||
{
|
{
|
||||||
$q = self::$queue;
|
$q = self::$queue;
|
||||||
while ($q && !$q->isEmpty()) {
|
while ($q && ! $q->isEmpty()) {
|
||||||
/** @var self $dfd */
|
/** @var self $dfd */
|
||||||
$dfd = $q->dequeue();
|
$dfd = $q->dequeue();
|
||||||
$dfd->run();
|
$dfd->run();
|
||||||
@ -38,7 +35,7 @@ class Deferred
|
|||||||
public function __construct(callable $callback)
|
public function __construct(callable $callback)
|
||||||
{
|
{
|
||||||
$this->callback = $callback;
|
$this->callback = $callback;
|
||||||
$this->promise = new SyncPromise();
|
$this->promise = new SyncPromise();
|
||||||
self::getQueue()->enqueue($this);
|
self::getQueue()->enqueue($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +44,7 @@ class Deferred
|
|||||||
return $this->promise->then($onFulfilled, $onRejected);
|
return $this->promise->then($onFulfilled, $onRejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function run()
|
public function run() : void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$cb = $this->callback;
|
$cb = $this->callback;
|
||||||
|
@ -35,6 +35,7 @@ use GraphQL\Utils\TypeInfo;
|
|||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
|
use function array_reduce;
|
||||||
use function array_values;
|
use function array_values;
|
||||||
use function get_class;
|
use function get_class;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
@ -68,7 +69,7 @@ class Executor
|
|||||||
self::$UNDEFINED = Utils::undefined();
|
self::$UNDEFINED = Utils::undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->exeContext = $context;
|
$this->exeContext = $context;
|
||||||
$this->subFieldCache = new \SplObjectStorage();
|
$this->subFieldCache = new \SplObjectStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +286,7 @@ class Executor
|
|||||||
// field and its descendants will be omitted, and sibling fields will still
|
// field and its descendants will be omitted, and sibling fields will still
|
||||||
// be executed. An execution which encounters errors will still result in a
|
// be executed. An execution which encounters errors will still result in a
|
||||||
// resolved Promise.
|
// resolved Promise.
|
||||||
$data = $this->executeOperation($this->exeContext->operation, $this->exeContext->rootValue);
|
$data = $this->executeOperation($this->exeContext->operation, $this->exeContext->rootValue);
|
||||||
$result = $this->buildResponse($data);
|
$result = $this->buildResponse($data);
|
||||||
|
|
||||||
// Note: we deviate here from the reference implementation a bit by always returning promise
|
// Note: we deviate here from the reference implementation a bit by always returning promise
|
||||||
@ -995,8 +996,7 @@ class Executor
|
|||||||
* If the callback does not return a Promise, then this function will also not
|
* If the callback does not return a Promise, then this function will also not
|
||||||
* return a Promise.
|
* return a Promise.
|
||||||
*
|
*
|
||||||
* @param mixed[] $values
|
* @param mixed[] $values
|
||||||
* @param \Closure $callback
|
|
||||||
* @param Promise|mixed|null $initialValue
|
* @param Promise|mixed|null $initialValue
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
@ -1291,23 +1291,18 @@ class Executor
|
|||||||
return $this->executeFields($returnType, $result, $path, $subFieldNodes);
|
return $this->executeFields($returnType, $result, $path, $subFieldNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function collectSubFields(ObjectType $returnType, $fieldNodes) : ArrayObject
|
||||||
* @param ObjectType $returnType
|
|
||||||
* @param $fieldNodes
|
|
||||||
* @return ArrayObject
|
|
||||||
*/
|
|
||||||
private function collectSubFields(ObjectType $returnType, $fieldNodes): ArrayObject
|
|
||||||
{
|
{
|
||||||
if (!isset($this->subFieldCache[$returnType])) {
|
if (! isset($this->subFieldCache[$returnType])) {
|
||||||
$this->subFieldCache[$returnType] = new \SplObjectStorage();
|
$this->subFieldCache[$returnType] = new \SplObjectStorage();
|
||||||
}
|
}
|
||||||
if (!isset($this->subFieldCache[$returnType][$fieldNodes])) {
|
if (! isset($this->subFieldCache[$returnType][$fieldNodes])) {
|
||||||
// Collect sub-fields to execute to complete this value.
|
// Collect sub-fields to execute to complete this value.
|
||||||
$subFieldNodes = new \ArrayObject();
|
$subFieldNodes = new \ArrayObject();
|
||||||
$visitedFragmentNames = new \ArrayObject();
|
$visitedFragmentNames = new \ArrayObject();
|
||||||
|
|
||||||
foreach ($fieldNodes as $fieldNode) {
|
foreach ($fieldNodes as $fieldNode) {
|
||||||
if (!isset($fieldNode->selectionSet)) {
|
if (! isset($fieldNode->selectionSet)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ namespace GraphQL\Executor\Promise;
|
|||||||
|
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
|
use React\Promise\Promise as ReactPromise;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience wrapper for promises represented by Promise Adapter
|
* Convenience wrapper for promises represented by Promise Adapter
|
||||||
|
182
src/GraphQL.php
182
src/GraphQL.php
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL;
|
namespace GraphQL;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -6,15 +9,19 @@ use GraphQL\Executor\ExecutionResult;
|
|||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
||||||
use GraphQL\Executor\Promise\Promise;
|
use GraphQL\Executor\Promise\Promise;
|
||||||
|
use GraphQL\Executor\Promise\PromiseAdapter;
|
||||||
use GraphQL\Language\AST\DocumentNode;
|
use GraphQL\Language\AST\DocumentNode;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\Source;
|
use GraphQL\Language\Source;
|
||||||
use GraphQL\Executor\Promise\PromiseAdapter;
|
|
||||||
use GraphQL\Type\Definition\Directive;
|
use GraphQL\Type\Definition\Directive;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema as SchemaType;
|
||||||
use GraphQL\Validator\DocumentValidator;
|
use GraphQL\Validator\DocumentValidator;
|
||||||
use GraphQL\Validator\Rules\ValidationRule;
|
|
||||||
use GraphQL\Validator\Rules\QueryComplexity;
|
use GraphQL\Validator\Rules\QueryComplexity;
|
||||||
|
use GraphQL\Validator\Rules\ValidationRule;
|
||||||
|
use function array_values;
|
||||||
|
use function trigger_error;
|
||||||
|
use const E_USER_DEPRECATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary facade for fulfilling GraphQL operations.
|
* This is the primary facade for fulfilling GraphQL operations.
|
||||||
@ -58,32 +65,35 @@ class GraphQL
|
|||||||
* queries which are validated before persisting and assumed valid during execution)
|
* queries which are validated before persisting and assumed valid during execution)
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param \GraphQL\Type\Schema $schema
|
|
||||||
* @param string|DocumentNode $source
|
* @param string|DocumentNode $source
|
||||||
* @param mixed $rootValue
|
* @param mixed $rootValue
|
||||||
* @param mixed $context
|
* @param mixed $context
|
||||||
* @param array|null $variableValues
|
* @param mixed[]|null $variableValues
|
||||||
* @param string|null $operationName
|
* @param ValidationRule[] $validationRules
|
||||||
* @param callable $fieldResolver
|
|
||||||
* @param array $validationRules
|
|
||||||
*
|
|
||||||
* @return ExecutionResult
|
|
||||||
*/
|
*/
|
||||||
public static function executeQuery(
|
public static function executeQuery(
|
||||||
\GraphQL\Type\Schema $schema,
|
SchemaType $schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue = null,
|
$rootValue = null,
|
||||||
$context = null,
|
$context = null,
|
||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null,
|
?string $operationName = null,
|
||||||
callable $fieldResolver = null,
|
?callable $fieldResolver = null,
|
||||||
array $validationRules = null
|
?array $validationRules = null
|
||||||
)
|
) : ExecutionResult {
|
||||||
{
|
|
||||||
$promiseAdapter = new SyncPromiseAdapter();
|
$promiseAdapter = new SyncPromiseAdapter();
|
||||||
|
|
||||||
$promise = self::promiseToExecute($promiseAdapter, $schema, $source, $rootValue, $context,
|
$promise = self::promiseToExecute(
|
||||||
$variableValues, $operationName, $fieldResolver, $validationRules);
|
$promiseAdapter,
|
||||||
|
$schema,
|
||||||
|
$source,
|
||||||
|
$rootValue,
|
||||||
|
$context,
|
||||||
|
$variableValues,
|
||||||
|
$operationName,
|
||||||
|
$fieldResolver,
|
||||||
|
$validationRules
|
||||||
|
);
|
||||||
|
|
||||||
return $promiseAdapter->wait($promise);
|
return $promiseAdapter->wait($promise);
|
||||||
}
|
}
|
||||||
@ -93,30 +103,23 @@ class GraphQL
|
|||||||
* Useful for Async PHP platforms.
|
* Useful for Async PHP platforms.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @param PromiseAdapter $promiseAdapter
|
* @param string|DocumentNode $source
|
||||||
* @param \GraphQL\Type\Schema $schema
|
* @param mixed $rootValue
|
||||||
* @param string|DocumentNode $source
|
* @param mixed $context
|
||||||
* @param mixed $rootValue
|
* @param mixed[]|null $variableValues
|
||||||
* @param mixed $context
|
* @param ValidationRule[]|null $validationRules
|
||||||
* @param array|null $variableValues
|
|
||||||
* @param string|null $operationName
|
|
||||||
* @param callable $fieldResolver
|
|
||||||
* @param array $validationRules
|
|
||||||
*
|
|
||||||
* @return Promise
|
|
||||||
*/
|
*/
|
||||||
public static function promiseToExecute(
|
public static function promiseToExecute(
|
||||||
PromiseAdapter $promiseAdapter,
|
PromiseAdapter $promiseAdapter,
|
||||||
\GraphQL\Type\Schema $schema,
|
SchemaType $schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue = null,
|
$rootValue = null,
|
||||||
$context = null,
|
$context = null,
|
||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null,
|
?string $operationName = null,
|
||||||
callable $fieldResolver = null,
|
?callable $fieldResolver = null,
|
||||||
array $validationRules = null
|
?array $validationRules = null
|
||||||
)
|
) : Promise {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
if ($source instanceof DocumentNode) {
|
if ($source instanceof DocumentNode) {
|
||||||
$documentNode = $source;
|
$documentNode = $source;
|
||||||
@ -125,36 +128,38 @@ class GraphQL
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
if (!empty($validationRules)) {
|
if (empty($validationRules)) {
|
||||||
foreach ($validationRules as $rule) {
|
|
||||||
if ($rule instanceof QueryComplexity) {
|
|
||||||
$rule->setRawVariableValues($variableValues);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/** @var QueryComplexity $queryComplexity */
|
/** @var QueryComplexity $queryComplexity */
|
||||||
$queryComplexity = DocumentValidator::getRule(QueryComplexity::class);
|
$queryComplexity = DocumentValidator::getRule(QueryComplexity::class);
|
||||||
$queryComplexity->setRawVariableValues($variableValues);
|
$queryComplexity->setRawVariableValues($variableValues);
|
||||||
|
} else {
|
||||||
|
foreach ($validationRules as $rule) {
|
||||||
|
if (! ($rule instanceof QueryComplexity)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rule->setRawVariableValues($variableValues);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$validationErrors = DocumentValidator::validate($schema, $documentNode, $validationRules);
|
$validationErrors = DocumentValidator::validate($schema, $documentNode, $validationRules);
|
||||||
|
|
||||||
if (!empty($validationErrors)) {
|
if (! empty($validationErrors)) {
|
||||||
return $promiseAdapter->createFulfilled(
|
return $promiseAdapter->createFulfilled(
|
||||||
new ExecutionResult(null, $validationErrors)
|
new ExecutionResult(null, $validationErrors)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return Executor::promiseToExecute(
|
|
||||||
$promiseAdapter,
|
|
||||||
$schema,
|
|
||||||
$documentNode,
|
|
||||||
$rootValue,
|
|
||||||
$context,
|
|
||||||
$variableValues,
|
|
||||||
$operationName,
|
|
||||||
$fieldResolver
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Executor::promiseToExecute(
|
||||||
|
$promiseAdapter,
|
||||||
|
$schema,
|
||||||
|
$documentNode,
|
||||||
|
$rootValue,
|
||||||
|
$context,
|
||||||
|
$variableValues,
|
||||||
|
$operationName,
|
||||||
|
$fieldResolver
|
||||||
|
);
|
||||||
} catch (Error $e) {
|
} catch (Error $e) {
|
||||||
return $promiseAdapter->createFulfilled(
|
return $promiseAdapter->createFulfilled(
|
||||||
new ExecutionResult(null, [$e])
|
new ExecutionResult(null, [$e])
|
||||||
@ -165,29 +170,28 @@ class GraphQL
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use executeQuery()->toArray() instead
|
* @deprecated Use executeQuery()->toArray() instead
|
||||||
*
|
*
|
||||||
* @param \GraphQL\Type\Schema $schema
|
|
||||||
* @param string|DocumentNode $source
|
* @param string|DocumentNode $source
|
||||||
* @param mixed $rootValue
|
* @param mixed $rootValue
|
||||||
* @param mixed $contextValue
|
* @param mixed $contextValue
|
||||||
* @param array|null $variableValues
|
* @param mixed[]|null $variableValues
|
||||||
* @param string|null $operationName
|
* @return Promise|mixed[]
|
||||||
* @return Promise|array
|
|
||||||
*/
|
*/
|
||||||
public static function execute(
|
public static function execute(
|
||||||
\GraphQL\Type\Schema $schema,
|
SchemaType $schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue = null,
|
$rootValue = null,
|
||||||
$contextValue = null,
|
$contextValue = null,
|
||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null
|
?string $operationName = null
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
trigger_error(
|
trigger_error(
|
||||||
__METHOD__ . ' is deprecated, use GraphQL::executeQuery()->toArray() as a quick replacement',
|
__METHOD__ . ' is deprecated, use GraphQL::executeQuery()->toArray() as a quick replacement',
|
||||||
E_USER_DEPRECATED
|
E_USER_DEPRECATED
|
||||||
);
|
);
|
||||||
$result = self::promiseToExecute(
|
|
||||||
$promiseAdapter = Executor::getPromiseAdapter(),
|
$promiseAdapter = Executor::getPromiseAdapter();
|
||||||
|
$result = self::promiseToExecute(
|
||||||
|
$promiseAdapter,
|
||||||
$schema,
|
$schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue,
|
$rootValue,
|
||||||
@ -199,40 +203,40 @@ class GraphQL
|
|||||||
if ($promiseAdapter instanceof SyncPromiseAdapter) {
|
if ($promiseAdapter instanceof SyncPromiseAdapter) {
|
||||||
$result = $promiseAdapter->wait($result)->toArray();
|
$result = $promiseAdapter->wait($result)->toArray();
|
||||||
} else {
|
} else {
|
||||||
$result = $result->then(function(ExecutionResult $r) {
|
$result = $result->then(function (ExecutionResult $r) {
|
||||||
return $r->toArray();
|
return $r->toArray();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated renamed to executeQuery()
|
* @deprecated renamed to executeQuery()
|
||||||
*
|
*
|
||||||
* @param \GraphQL\Type\Schema $schema
|
|
||||||
* @param string|DocumentNode $source
|
* @param string|DocumentNode $source
|
||||||
* @param mixed $rootValue
|
* @param mixed $rootValue
|
||||||
* @param mixed $contextValue
|
* @param mixed $contextValue
|
||||||
* @param array|null $variableValues
|
* @param mixed[]|null $variableValues
|
||||||
* @param string|null $operationName
|
|
||||||
*
|
*
|
||||||
* @return ExecutionResult|Promise
|
* @return ExecutionResult|Promise
|
||||||
*/
|
*/
|
||||||
public static function executeAndReturnResult(
|
public static function executeAndReturnResult(
|
||||||
\GraphQL\Type\Schema $schema,
|
SchemaType $schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue = null,
|
$rootValue = null,
|
||||||
$contextValue = null,
|
$contextValue = null,
|
||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null
|
?string $operationName = null
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
trigger_error(
|
trigger_error(
|
||||||
__METHOD__ . ' is deprecated, use GraphQL::executeQuery() as a quick replacement',
|
__METHOD__ . ' is deprecated, use GraphQL::executeQuery() as a quick replacement',
|
||||||
E_USER_DEPRECATED
|
E_USER_DEPRECATED
|
||||||
);
|
);
|
||||||
$result = self::promiseToExecute(
|
|
||||||
$promiseAdapter = Executor::getPromiseAdapter(),
|
$promiseAdapter = Executor::getPromiseAdapter();
|
||||||
|
$result = self::promiseToExecute(
|
||||||
|
$promiseAdapter,
|
||||||
$schema,
|
$schema,
|
||||||
$source,
|
$source,
|
||||||
$rootValue,
|
$rootValue,
|
||||||
@ -240,9 +244,11 @@ class GraphQL
|
|||||||
$variableValues,
|
$variableValues,
|
||||||
$operationName
|
$operationName
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($promiseAdapter instanceof SyncPromiseAdapter) {
|
if ($promiseAdapter instanceof SyncPromiseAdapter) {
|
||||||
$result = $promiseAdapter->wait($result);
|
$result = $promiseAdapter->wait($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +258,7 @@ class GraphQL
|
|||||||
* @api
|
* @api
|
||||||
* @return Directive[]
|
* @return Directive[]
|
||||||
*/
|
*/
|
||||||
public static function getStandardDirectives()
|
public static function getStandardDirectives() : array
|
||||||
{
|
{
|
||||||
return array_values(Directive::getInternalDirectives());
|
return array_values(Directive::getInternalDirectives());
|
||||||
}
|
}
|
||||||
@ -263,7 +269,7 @@ class GraphQL
|
|||||||
* @api
|
* @api
|
||||||
* @return Type[]
|
* @return Type[]
|
||||||
*/
|
*/
|
||||||
public static function getStandardTypes()
|
public static function getStandardTypes() : array
|
||||||
{
|
{
|
||||||
return array_values(Type::getInternalTypes());
|
return array_values(Type::getInternalTypes());
|
||||||
}
|
}
|
||||||
@ -274,23 +280,17 @@ class GraphQL
|
|||||||
* @api
|
* @api
|
||||||
* @return ValidationRule[]
|
* @return ValidationRule[]
|
||||||
*/
|
*/
|
||||||
public static function getStandardValidationRules()
|
public static function getStandardValidationRules() : array
|
||||||
{
|
{
|
||||||
return array_values(DocumentValidator::defaultRules());
|
return array_values(DocumentValidator::defaultRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function setDefaultFieldResolver(callable $fn) : void
|
||||||
* @param callable $fn
|
|
||||||
*/
|
|
||||||
public static function setDefaultFieldResolver(callable $fn)
|
|
||||||
{
|
{
|
||||||
Executor::setDefaultFieldResolver($fn);
|
Executor::setDefaultFieldResolver($fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function setPromiseAdapter(?PromiseAdapter $promiseAdapter = null) : void
|
||||||
* @param PromiseAdapter|null $promiseAdapter
|
|
||||||
*/
|
|
||||||
public static function setPromiseAdapter(PromiseAdapter $promiseAdapter = null)
|
|
||||||
{
|
{
|
||||||
Executor::setPromiseAdapter($promiseAdapter);
|
Executor::setPromiseAdapter($promiseAdapter);
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ class GraphQL
|
|||||||
* @deprecated Renamed to getStandardDirectives
|
* @deprecated Renamed to getStandardDirectives
|
||||||
* @return Directive[]
|
* @return Directive[]
|
||||||
*/
|
*/
|
||||||
public static function getInternalDirectives()
|
public static function getInternalDirectives() : array
|
||||||
{
|
{
|
||||||
return self::getStandardDirectives();
|
return self::getStandardDirectives();
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,8 @@ class Visitor
|
|||||||
$visitFn = self::getVisitFn($visitor, $node->kind, $isLeaving);
|
$visitFn = self::getVisitFn($visitor, $node->kind, $isLeaving);
|
||||||
|
|
||||||
if ($visitFn) {
|
if ($visitFn) {
|
||||||
$result = call_user_func($visitFn, $node, $key, $parent, $path, $ancestors);
|
$result = call_user_func($visitFn, $node, $key, $parent, $path, $ancestors);
|
||||||
|
$editValue = null;
|
||||||
|
|
||||||
if ($result !== null) {
|
if ($result !== null) {
|
||||||
if ($result instanceof VisitorOperation) {
|
if ($result instanceof VisitorOperation) {
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL;
|
namespace GraphQL;
|
||||||
|
|
||||||
|
use function trigger_error;
|
||||||
|
use const E_USER_DEPRECATED;
|
||||||
|
|
||||||
trigger_error(
|
trigger_error(
|
||||||
'GraphQL\Schema is moved to GraphQL\Type\Schema',
|
'GraphQL\Schema is moved to GraphQL\Type\Schema',
|
||||||
E_USER_DEPRECATED
|
E_USER_DEPRECATED
|
||||||
|
@ -4,11 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace GraphQL\Server;
|
namespace GraphQL\Server;
|
||||||
|
|
||||||
use const CASE_LOWER;
|
|
||||||
use function array_change_key_case;
|
use function array_change_key_case;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
use function json_decode;
|
use function json_decode;
|
||||||
use function json_last_error;
|
use function json_last_error;
|
||||||
|
use const CASE_LOWER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure representing parsed HTTP parameters for GraphQL operation
|
* Structure representing parsed HTTP parameters for GraphQL operation
|
||||||
|
@ -8,7 +8,7 @@ use GraphQL\Error\FormattedError;
|
|||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Executor\Promise\Promise;
|
use GraphQL\Executor\Promise\Promise;
|
||||||
use GraphQL\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
@ -21,7 +21,7 @@ class InputObjectField
|
|||||||
/** @var string|null */
|
/** @var string|null */
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/** @var callback|InputType */
|
/** @var callable|InputType */
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
/** @var InputValueDefinitionNode|null */
|
/** @var InputValueDefinitionNode|null */
|
||||||
|
@ -12,7 +12,6 @@ use function is_array;
|
|||||||
use function is_callable;
|
use function is_callable;
|
||||||
use function is_string;
|
use function is_string;
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
use function spritnf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class InputObjectType
|
* Class InputObjectType
|
||||||
@ -70,7 +69,7 @@ class InputObjectType extends Type implements InputType, NamedType
|
|||||||
|
|
||||||
if (! is_array($fields)) {
|
if (! is_array($fields)) {
|
||||||
throw new InvariantViolation(
|
throw new InvariantViolation(
|
||||||
spritnf('%s fields must be an array or a callable which returns such an array.', $this->name)
|
sprintf('%s fields must be an array or a callable which returns such an array.', $this->name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ values. Int can represent values between -(2^31) and 2^31 - 1. ';
|
|||||||
}
|
}
|
||||||
$int = intval($num);
|
$int = intval($num);
|
||||||
// int cast with == used for performance reasons
|
// int cast with == used for performance reasons
|
||||||
// @codingStandardsIgnoreLine
|
// phpcs:ignore
|
||||||
if ($int != $num) {
|
if ($int != $num) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Int cannot represent non-integer value: ' .
|
'Int cannot represent non-integer value: ' .
|
||||||
|
@ -255,7 +255,7 @@ class AST
|
|||||||
}
|
}
|
||||||
if (is_float($serialized)) {
|
if (is_float($serialized)) {
|
||||||
// int cast with == used for performance reasons
|
// int cast with == used for performance reasons
|
||||||
// @codingStandardsIgnoreLine
|
// phpcs:ignore
|
||||||
if ((int) $serialized == $serialized) {
|
if ((int) $serialized == $serialized) {
|
||||||
return new IntValueNode(['value' => $serialized]);
|
return new IntValueNode(['value' => $serialized]);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@ namespace GraphQL\Validator\Rules;
|
|||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Language\AST\DirectiveNode;
|
use GraphQL\Language\AST\DirectiveNode;
|
||||||
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
||||||
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\NodeKind;
|
use GraphQL\Language\AST\NodeKind;
|
||||||
|
use GraphQL\Language\AST\NodeList;
|
||||||
use GraphQL\Language\DirectiveLocation;
|
use GraphQL\Language\DirectiveLocation;
|
||||||
use GraphQL\Validator\ValidationContext;
|
use GraphQL\Validator\ValidationContext;
|
||||||
use function count;
|
use function count;
|
||||||
@ -59,7 +61,9 @@ class KnownDirectives extends ValidationRule
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param (Node|NodeList)[] $ancestors
|
* @param Node[]|NodeList[] $ancestors The type is actually (Node|NodeList)[] but this PSR-5 syntax is so far not supported by most of the tools
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getDirectiveLocationForASTPath(array $ancestors)
|
private function getDirectiveLocationForASTPath(array $ancestors)
|
||||||
{
|
{
|
||||||
|
@ -44,10 +44,10 @@ class VariablesDefaultValueAllowed extends ValidationRule
|
|||||||
|
|
||||||
return Visitor::skipNode();
|
return Visitor::skipNode();
|
||||||
},
|
},
|
||||||
NodeKind::SELECTION_SET => function (SelectionSetNode $node) use ($context) {
|
NodeKind::SELECTION_SET => function (SelectionSetNode $node) {
|
||||||
return Visitor::skipNode();
|
return Visitor::skipNode();
|
||||||
},
|
},
|
||||||
NodeKind::FRAGMENT_DEFINITION => function (FragmentDefinitionNode $node) use ($context) {
|
NodeKind::FRAGMENT_DEFINITION => function (FragmentDefinitionNode $node) {
|
||||||
return Visitor::skipNode();
|
return Visitor::skipNode();
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,79 +1,83 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Error\UserError;
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Error\Warning;
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Tests\Executor\TestClasses\Cat;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Dog;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Human;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
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;
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DESCRIBE: Execute: Handles execution of abstract types with promises
|
||||||
|
*/
|
||||||
class AbstractPromiseTest extends TestCase
|
class AbstractPromiseTest extends TestCase
|
||||||
{
|
{
|
||||||
// DESCRIBE: Execute: Handles execution of abstract types with promises
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('isTypeOf used to resolve runtime type for Interface')
|
* @see it('isTypeOf used to resolve runtime type for Interface')
|
||||||
*/
|
*/
|
||||||
public function testIsTypeOfUsedToResolveRuntimeTypeForInterface() : void
|
public function testIsTypeOfUsedToResolveRuntimeTypeForInterface() : void
|
||||||
{
|
{
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [ $PetType ],
|
'interfaces' => [$PetType],
|
||||||
'isTypeOf' => function($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return new Deferred(function() use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
return $obj instanceof Dog;
|
return $obj instanceof Dog;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => [ 'type' => Type::boolean() ],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [ $PetType ],
|
'interfaces' => [$PetType],
|
||||||
'isTypeOf' => function($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return new Deferred(function() use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => [ 'type' => Type::boolean() ],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function() {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false)
|
new Cat('Garfield', false),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [ $CatType, $DogType ]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -93,10 +97,10 @@ class AbstractPromiseTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
[ 'name' => 'Odie', 'woofs' => true ],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
[ 'name' => 'Garfield', 'meows' => false ]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
@ -107,58 +111,57 @@ class AbstractPromiseTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testIsTypeOfCanBeRejected() : void
|
public function testIsTypeOfCanBeRejected() : void
|
||||||
{
|
{
|
||||||
|
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'isTypeOf' => function () {
|
'isTypeOf' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw new UserError('We are testing this error');
|
throw new UserError('We are testing this error');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return new Deferred(function () use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false)
|
new Cat('Garfield', false),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$CatType, $DogType]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -176,21 +179,21 @@ class AbstractPromiseTest extends TestCase
|
|||||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [null, null]
|
'pets' => [null, null],
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'We are testing this error',
|
'message' => 'We are testing this error',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 0],
|
'path' => ['pets', 0],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'message' => 'We are testing this error',
|
'message' => 'We are testing this error',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 1]
|
'path' => ['pets', 1],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
@ -201,50 +204,49 @@ class AbstractPromiseTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
||||||
{
|
{
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return new Deferred(function () use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
return $obj instanceof Dog;
|
return $obj instanceof Dog;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return new Deferred(function () use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PetType = new UnionType([
|
$PetType = new UnionType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'types' => [$DogType, $CatType]
|
'types' => [$DogType, $CatType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -266,9 +268,9 @@ class AbstractPromiseTest extends TestCase
|
|||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
@ -280,7 +282,7 @@ class AbstractPromiseTest extends TestCase
|
|||||||
public function testResolveTypeOnInterfaceYieldsUsefulError() : void
|
public function testResolveTypeOnInterfaceYieldsUsefulError() : void
|
||||||
{
|
{
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
||||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||||
if ($obj instanceof Dog) {
|
if ($obj instanceof Dog) {
|
||||||
@ -292,58 +294,59 @@ class AbstractPromiseTest extends TestCase
|
|||||||
if ($obj instanceof Human) {
|
if ($obj instanceof Human) {
|
||||||
return $HumanType;
|
return $HumanType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$HumanType = new ObjectType([
|
$HumanType = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false),
|
new Cat('Garfield', false),
|
||||||
new Human('Jon')
|
new Human('Jon'),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$CatType, $DogType]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -361,20 +364,20 @@ class AbstractPromiseTest extends TestCase
|
|||||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false],
|
['name' => 'Garfield', 'meows' => false],
|
||||||
null
|
null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 2]
|
'path' => ['pets', 2],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
@ -385,32 +388,31 @@ class AbstractPromiseTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
||||||
{
|
{
|
||||||
|
|
||||||
$HumanType = new ObjectType([
|
$HumanType = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PetType = new UnionType([
|
$PetType = new UnionType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
||||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||||
if ($obj instanceof Dog) {
|
if ($obj instanceof Dog) {
|
||||||
@ -422,28 +424,29 @@ class AbstractPromiseTest extends TestCase
|
|||||||
if ($obj instanceof Human) {
|
if ($obj instanceof Human) {
|
||||||
return $HumanType;
|
return $HumanType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'types' => [$DogType, $CatType]
|
'types' => [$DogType, $CatType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false),
|
new Cat('Garfield', false),
|
||||||
new Human('Jon')
|
new Human('Jon'),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -462,20 +465,20 @@ class AbstractPromiseTest extends TestCase
|
|||||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false],
|
['name' => 'Garfield', 'meows' => false],
|
||||||
null
|
null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 2]
|
'path' => ['pets', 2],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
@ -487,7 +490,7 @@ class AbstractPromiseTest extends TestCase
|
|||||||
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
||||||
{
|
{
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function ($obj) {
|
'resolveType' => function ($obj) {
|
||||||
return new Deferred(function () use ($obj) {
|
return new Deferred(function () use ($obj) {
|
||||||
if ($obj instanceof Dog) {
|
if ($obj instanceof Dog) {
|
||||||
@ -496,49 +499,49 @@ class AbstractPromiseTest extends TestCase
|
|||||||
if ($obj instanceof Cat) {
|
if ($obj instanceof Cat) {
|
||||||
return 'Cat';
|
return 'Cat';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false)
|
new Cat('Garfield', false),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$CatType, $DogType]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -560,8 +563,8 @@ class AbstractPromiseTest extends TestCase
|
|||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false],
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
}
|
}
|
||||||
@ -571,53 +574,52 @@ class AbstractPromiseTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testResolveTypeCanBeCaught() : void
|
public function testResolveTypeCanBeCaught() : void
|
||||||
{
|
{
|
||||||
|
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function () {
|
'resolveType' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw new UserError('We are testing this error');
|
throw new UserError('We are testing this error');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false)
|
new Cat('Garfield', false),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$CatType, $DogType]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -635,21 +637,21 @@ class AbstractPromiseTest extends TestCase
|
|||||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [null, null]
|
'pets' => [null, null],
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'We are testing this error',
|
'message' => 'We are testing this error',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 0]
|
'path' => ['pets', 0],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'message' => 'We are testing this error',
|
'message' => 'We are testing this error',
|
||||||
'locations' => [['line' => 2, 'column' => 7]],
|
'locations' => [['line' => 2, 'column' => 7]],
|
||||||
'path' => ['pets', 1]
|
'path' => ['pets', 1],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Executor;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Tests\Executor\TestClasses\Cat;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Dog;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Human;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
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 PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute: Handles execution of abstract types
|
||||||
|
*/
|
||||||
class AbstractTest extends TestCase
|
class AbstractTest extends TestCase
|
||||||
{
|
{
|
||||||
// Execute: Handles execution of abstract types
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('isTypeOf used to resolve runtime type for Interface')
|
* @see it('isTypeOf used to resolve runtime type for Interface')
|
||||||
*/
|
*/
|
||||||
@ -25,48 +30,50 @@ class AbstractTest extends TestCase
|
|||||||
{
|
{
|
||||||
// isTypeOf used to resolve runtime type for Interface
|
// isTypeOf used to resolve runtime type for Interface
|
||||||
$petType = new InterfaceType([
|
$petType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Added to interface type when defined
|
// Added to interface type when defined
|
||||||
$dogType = new ObjectType([
|
$dogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$petType],
|
'interfaces' => [$petType],
|
||||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
'isTypeOf' => function ($obj) {
|
||||||
'fields' => [
|
return $obj instanceof Dog;
|
||||||
'name' => ['type' => Type::string()],
|
},
|
||||||
'woofs' => ['type' => Type::boolean()]
|
'fields' => [
|
||||||
]
|
'name' => ['type' => Type::string()],
|
||||||
|
'woofs' => ['type' => Type::boolean()],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$catType = new ObjectType([
|
$catType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$petType],
|
'interfaces' => [$petType],
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($petType),
|
'type' => Type::listOf($petType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$catType, $dogType]
|
'types' => [$catType, $dogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -84,8 +91,8 @@ class AbstractTest extends TestCase
|
|||||||
$expected = new ExecutionResult([
|
$expected = new ExecutionResult([
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = Executor::execute($schema, Parser::parse($query));
|
$result = Executor::execute($schema, Parser::parse($query));
|
||||||
@ -98,42 +105,44 @@ class AbstractTest extends TestCase
|
|||||||
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void
|
||||||
{
|
{
|
||||||
$dogType = new ObjectType([
|
$dogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
'isTypeOf' => function ($obj) {
|
||||||
'fields' => [
|
return $obj instanceof Dog;
|
||||||
'name' => ['type' => Type::string()],
|
},
|
||||||
'woofs' => ['type' => Type::boolean()]
|
'fields' => [
|
||||||
]
|
'name' => ['type' => Type::string()],
|
||||||
|
'woofs' => ['type' => Type::boolean()],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$catType = new ObjectType([
|
$catType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$petType = new UnionType([
|
$petType = new UnionType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'types' => [$dogType, $catType]
|
'types' => [$dogType, $catType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($petType),
|
'type' => Type::listOf($petType),
|
||||||
'resolve' => function() {
|
'resolve' => function () {
|
||||||
return [ new Dog('Odie', true), new Cat('Garfield', false) ];
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -151,8 +160,8 @@ class AbstractTest extends TestCase
|
|||||||
$expected = new ExecutionResult([
|
$expected = new ExecutionResult([
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($schema, Parser::parse($query)));
|
$this->assertEquals($expected, Executor::execute($schema, Parser::parse($query)));
|
||||||
@ -161,14 +170,14 @@ class AbstractTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @see it('resolveType on Interface yields useful error')
|
* @see it('resolveType on Interface yields useful error')
|
||||||
*/
|
*/
|
||||||
function testResolveTypeOnInterfaceYieldsUsefulError()
|
public function testResolveTypeOnInterfaceYieldsUsefulError() : void
|
||||||
{
|
{
|
||||||
$DogType = null;
|
$DogType = null;
|
||||||
$CatType = null;
|
$CatType = null;
|
||||||
$HumanType = null;
|
$HumanType = null;
|
||||||
|
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
||||||
if ($obj instanceof Dog) {
|
if ($obj instanceof Dog) {
|
||||||
return $DogType;
|
return $DogType;
|
||||||
@ -179,58 +188,58 @@ class AbstractTest extends TestCase
|
|||||||
if ($obj instanceof Human) {
|
if ($obj instanceof Human) {
|
||||||
return $HumanType;
|
return $HumanType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$HumanType = new ObjectType([
|
$HumanType = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false),
|
new Cat('Garfield', false),
|
||||||
new Human('Jon')
|
new Human('Jon'),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$DogType, $CatType]
|
'types' => [$DogType, $CatType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
pets {
|
pets {
|
||||||
name
|
name
|
||||||
@ -244,20 +253,21 @@ class AbstractTest extends TestCase
|
|||||||
}';
|
}';
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false],
|
['name' => 'Garfield', 'meows' => false],
|
||||||
null
|
null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||||
'locations' => [['line' => 2, 'column' => 11]],
|
'locations' => [['line' => 2, 'column' => 11]],
|
||||||
'path' => ['pets', 2]
|
'path' => ['pets', 2],
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$actual = GraphQL::executeQuery($schema, $query)->toArray(true);
|
$actual = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||||
|
|
||||||
$this->assertArraySubset($expected, $actual);
|
$this->assertArraySubset($expected, $actual);
|
||||||
}
|
}
|
||||||
@ -268,30 +278,30 @@ class AbstractTest extends TestCase
|
|||||||
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
public function testResolveTypeOnUnionYieldsUsefulError() : void
|
||||||
{
|
{
|
||||||
$HumanType = new ObjectType([
|
$HumanType = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PetType = new UnionType([
|
$PetType = new UnionType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
||||||
if ($obj instanceof Dog) {
|
if ($obj instanceof Dog) {
|
||||||
return $DogType;
|
return $DogType;
|
||||||
@ -303,25 +313,25 @@ class AbstractTest extends TestCase
|
|||||||
return $HumanType;
|
return $HumanType;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'types' => [$DogType, $CatType]
|
'types' => [$DogType, $CatType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false),
|
new Cat('Garfield', false),
|
||||||
new Human('Jon')
|
new Human('Jon'),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -337,22 +347,27 @@ class AbstractTest extends TestCase
|
|||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
|
||||||
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
$result = GraphQL::executeQuery($schema, $query)->toArray(true);
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie',
|
[
|
||||||
'woofs' => true],
|
'name' => 'Odie',
|
||||||
['name' => 'Garfield',
|
'woofs' => true,
|
||||||
'meows' => false],
|
],
|
||||||
null
|
[
|
||||||
]
|
'name' => 'Garfield',
|
||||||
|
'meows' => false,
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||||
'locations' => [['line' => 2, 'column' => 11]],
|
'locations' => [['line' => 2, 'column' => 11]],
|
||||||
'path' => ['pets', 2]
|
'path' => ['pets', 2],
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
}
|
}
|
||||||
@ -363,25 +378,25 @@ class AbstractTest extends TestCase
|
|||||||
public function testReturningInvalidValueFromResolveTypeYieldsUsefulError() : void
|
public function testReturningInvalidValueFromResolveTypeYieldsUsefulError() : void
|
||||||
{
|
{
|
||||||
$fooInterface = new InterfaceType([
|
$fooInterface = new InterfaceType([
|
||||||
'name' => 'FooInterface',
|
'name' => 'FooInterface',
|
||||||
'fields' => ['bar' => ['type' => Type::string()]],
|
'fields' => ['bar' => ['type' => Type::string()]],
|
||||||
'resolveType' => function () {
|
'resolveType' => function () {
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$fooObject = new ObjectType([
|
$fooObject = new ObjectType([
|
||||||
'name' => 'FooObject',
|
'name' => 'FooObject',
|
||||||
'fields' => ['bar' => ['type' => Type::string()]],
|
'fields' => ['bar' => ['type' => Type::string()]],
|
||||||
'interfaces' => [$fooInterface],
|
'interfaces' => [$fooInterface],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'foo' => [
|
'foo' => [
|
||||||
'type' => $fooInterface,
|
'type' => $fooInterface,
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return 'dummy';
|
return 'dummy';
|
||||||
},
|
},
|
||||||
@ -394,18 +409,18 @@ class AbstractTest extends TestCase
|
|||||||
$result = GraphQL::executeQuery($schema, '{ foo { bar } }');
|
$result = GraphQL::executeQuery($schema, '{ foo { bar } }');
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['foo' => null],
|
'data' => ['foo' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Internal server error',
|
'message' => 'Internal server error',
|
||||||
'debugMessage' =>
|
'debugMessage' =>
|
||||||
'Abstract type FooInterface must resolve to an Object type at ' .
|
'Abstract type FooInterface must resolve to an Object type at ' .
|
||||||
'runtime for field Query.foo with value "dummy", received "[]". ' .
|
'runtime for field Query.foo with value "dummy", received "[]". ' .
|
||||||
'Either the FooInterface type should provide a "resolveType" ' .
|
'Either the FooInterface type should provide a "resolveType" ' .
|
||||||
'function or each possible type should provide an "isTypeOf" function.',
|
'function or each possible type should provide an "isTypeOf" function.',
|
||||||
'locations' => [['line' => 1, 'column' => 3]],
|
'locations' => [['line' => 1, 'column' => 3]],
|
||||||
'path' => ['foo'],
|
'path' => ['foo'],
|
||||||
'category' => 'internal',
|
'category' => 'internal',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
@ -418,51 +433,56 @@ class AbstractTest extends TestCase
|
|||||||
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
public function testResolveTypeAllowsResolvingWithTypeName() : void
|
||||||
{
|
{
|
||||||
$PetType = new InterfaceType([
|
$PetType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function($obj) {
|
'resolveType' => function ($obj) {
|
||||||
if ($obj instanceof Dog) return 'Dog';
|
if ($obj instanceof Dog) {
|
||||||
if ($obj instanceof Cat) return 'Cat';
|
return 'Dog';
|
||||||
|
}
|
||||||
|
if ($obj instanceof Cat) {
|
||||||
|
return 'Cat';
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [ $PetType ],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => [ 'type' => Type::boolean() ],
|
'woofs' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [ $PetType ],
|
'interfaces' => [$PetType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [ 'type' => Type::string() ],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => [ 'type' => Type::boolean() ],
|
'meows' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($PetType),
|
'type' => Type::listOf($PetType),
|
||||||
'resolve' => function() {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
new Dog('Odie', true),
|
new Dog('Odie', true),
|
||||||
new Cat('Garfield', false)
|
new Cat('Garfield', false),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [ $CatType, $DogType ]
|
'types' => [$CatType, $DogType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -479,51 +499,52 @@ class AbstractTest extends TestCase
|
|||||||
|
|
||||||
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
$result = GraphQL::executeQuery($schema, $query)->toArray();
|
||||||
|
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'data' => [
|
[
|
||||||
'pets' => [
|
'data' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
'pets' => [
|
||||||
['name' => 'Garfield', 'meows' => false]
|
['name' => 'Odie', 'woofs' => true],
|
||||||
]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
]
|
],
|
||||||
], $result);
|
],
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHintsOnConflictingTypeInstancesInResolveType() : void
|
public function testHintsOnConflictingTypeInstancesInResolveType() : void
|
||||||
{
|
{
|
||||||
$createTest = function() use (&$iface) {
|
$createTest = function () use (&$iface) {
|
||||||
return new ObjectType([
|
return new ObjectType([
|
||||||
'name' => 'Test',
|
'name' => 'Test',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'a' => Type::string()
|
'a' => Type::string(),
|
||||||
],
|
],
|
||||||
'interfaces' => function() use ($iface) {
|
'interfaces' => function () use ($iface) {
|
||||||
return [$iface];
|
return [$iface];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$iface = new InterfaceType([
|
$iface = new InterfaceType([
|
||||||
'name' => 'Node',
|
'name' => 'Node',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'a' => Type::string()
|
'a' => Type::string(),
|
||||||
],
|
],
|
||||||
'resolveType' => function() use (&$createTest) {
|
'resolveType' => function () use (&$createTest) {
|
||||||
return $createTest();
|
return $createTest();
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'node' => $iface,
|
'node' => $iface,
|
||||||
'test' => $createTest()
|
'test' => $createTest(),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema(['query' => $query]);
|
||||||
'query' => $query,
|
|
||||||
]);
|
|
||||||
$schema->assertValid();
|
$schema->assertValid();
|
||||||
|
|
||||||
$query = '
|
$query = '
|
||||||
@ -537,9 +558,9 @@ class AbstractTest extends TestCase
|
|||||||
$result = Executor::execute($schema, Parser::parse($query), ['node' => ['a' => 'value']]);
|
$result = Executor::execute($schema, Parser::parse($query), ['node' => ['a' => 'value']]);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'Schema must contain unique named types but contains multiple types named "Test". '.
|
'Schema must contain unique named types but contains multiple types named "Test". ' .
|
||||||
'Make sure that `resolveType` function of abstract type "Node" returns the same type instance '.
|
'Make sure that `resolveType` function of abstract type "Node" returns the same type instance ' .
|
||||||
'as referenced anywhere else within the schema '.
|
'as referenced anywhere else within the schema ' .
|
||||||
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
||||||
$result->errors[0]->getMessage()
|
$result->errors[0]->getMessage()
|
||||||
);
|
);
|
||||||
|
@ -1,33 +1,44 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Executor;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
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 GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function in_array;
|
||||||
|
|
||||||
class DeferredFieldsTest extends TestCase
|
class DeferredFieldsTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var ObjectType */
|
||||||
private $userType;
|
private $userType;
|
||||||
|
|
||||||
|
/** @var ObjectType */
|
||||||
private $storyType;
|
private $storyType;
|
||||||
|
|
||||||
|
/** @var ObjectType */
|
||||||
private $categoryType;
|
private $categoryType;
|
||||||
|
|
||||||
|
/** @var */
|
||||||
private $path;
|
private $path;
|
||||||
|
|
||||||
|
/** @var mixed[][] */
|
||||||
private $storyDataSource;
|
private $storyDataSource;
|
||||||
|
|
||||||
|
/** @var mixed[][] */
|
||||||
private $userDataSource;
|
private $userDataSource;
|
||||||
|
|
||||||
|
/** @var mixed[][] */
|
||||||
private $categoryDataSource;
|
private $categoryDataSource;
|
||||||
|
|
||||||
|
/** @var ObjectType */
|
||||||
private $queryType;
|
private $queryType;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
@ -54,127 +65,152 @@ class DeferredFieldsTest extends TestCase
|
|||||||
$this->categoryDataSource = [
|
$this->categoryDataSource = [
|
||||||
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
|
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
|
||||||
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3],
|
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3],
|
||||||
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9]
|
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->path = [];
|
$this->path = [];
|
||||||
$this->userType = new ObjectType([
|
$this->userType = new ObjectType([
|
||||||
'name' => 'User',
|
'name' => 'User',
|
||||||
'fields' => function() {
|
'fields' => function () {
|
||||||
return [
|
return [
|
||||||
'name' => [
|
'name' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return $user['name'];
|
return $user['name'];
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'bestFriend' => [
|
'bestFriend' => [
|
||||||
'type' => $this->userType,
|
'type' => $this->userType,
|
||||||
'resolve' => function($user, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return new Deferred(function() use ($user) {
|
return new Deferred(function () use ($user) {
|
||||||
$this->path[] = 'deferred-for-best-friend-of-' . $user['id'];
|
$this->path[] = 'deferred-for-best-friend-of-' . $user['id'];
|
||||||
return Utils::find($this->userDataSource, function($entry) use ($user) {
|
|
||||||
return $entry['id'] === $user['bestFriendId'];
|
return Utils::find(
|
||||||
});
|
$this->userDataSource,
|
||||||
|
function ($entry) use ($user) {
|
||||||
|
return $entry['id'] === $user['bestFriendId'];
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->storyType = new ObjectType([
|
$this->storyType = new ObjectType([
|
||||||
'name' => 'Story',
|
'name' => 'Story',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'title' => [
|
'title' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($entry, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($entry, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
|
||||||
return $entry['title'];
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'author' => [
|
|
||||||
'type' => $this->userType,
|
|
||||||
'resolve' => function($story, $args, $context, ResolveInfo $info) {
|
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return new Deferred(function() use ($story) {
|
return $entry['title'];
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'author' => [
|
||||||
|
'type' => $this->userType,
|
||||||
|
'resolve' => function ($story, $args, $context, ResolveInfo $info) {
|
||||||
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
|
return new Deferred(function () use ($story) {
|
||||||
$this->path[] = 'deferred-for-story-' . $story['id'] . '-author';
|
$this->path[] = 'deferred-for-story-' . $story['id'] . '-author';
|
||||||
return Utils::find($this->userDataSource, function($entry) use ($story) {
|
|
||||||
return $entry['id'] === $story['authorId'];
|
return Utils::find(
|
||||||
});
|
$this->userDataSource,
|
||||||
|
function ($entry) use ($story) {
|
||||||
|
return $entry['id'] === $story['authorId'];
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->categoryType = new ObjectType([
|
$this->categoryType = new ObjectType([
|
||||||
'name' => 'Category',
|
'name' => 'Category',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => [
|
'name' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return $category['name'];
|
return $category['name'];
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
'stories' => [
|
'stories' => [
|
||||||
'type' => Type::listOf($this->storyType),
|
'type' => Type::listOf($this->storyType),
|
||||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
return Utils::filter($this->storyDataSource, function($story) use ($category) {
|
|
||||||
return in_array($category['id'], $story['categoryIds']);
|
return Utils::filter(
|
||||||
});
|
$this->storyDataSource,
|
||||||
}
|
function ($story) use ($category) {
|
||||||
|
return in_array($category['id'], $story['categoryIds']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'topStory' => [
|
'topStory' => [
|
||||||
'type' => $this->storyType,
|
'type' => $this->storyType,
|
||||||
'resolve' => function($category, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($category, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return new Deferred(function () use ($category) {
|
return new Deferred(function () use ($category) {
|
||||||
$this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory';
|
$this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory';
|
||||||
return Utils::find($this->storyDataSource, function($story) use ($category) {
|
|
||||||
return $story['id'] === $category['topStoryId'];
|
return Utils::find(
|
||||||
});
|
$this->storyDataSource,
|
||||||
|
function ($story) use ($category) {
|
||||||
|
return $story['id'] === $category['topStoryId'];
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->queryType = new ObjectType([
|
$this->queryType = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'topStories' => [
|
'topStories' => [
|
||||||
'type' => Type::listOf($this->storyType),
|
'type' => Type::listOf($this->storyType),
|
||||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
return Utils::filter($this->storyDataSource, function($story) {
|
|
||||||
return $story['id'] % 2 === 1;
|
return Utils::filter(
|
||||||
});
|
$this->storyDataSource,
|
||||||
}
|
function ($story) {
|
||||||
|
return $story['id'] % 2 === 1;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'featuredCategory' => [
|
'featuredCategory' => [
|
||||||
'type' => $this->categoryType,
|
'type' => $this->categoryType,
|
||||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return $this->categoryDataSource[0];
|
return $this->categoryDataSource[0];
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'categories' => [
|
'categories' => [
|
||||||
'type' => Type::listOf($this->categoryType),
|
'type' => Type::listOf($this->categoryType),
|
||||||
'resolve' => function($val, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($val, $args, $context, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return $this->categoryDataSource;
|
return $this->categoryDataSource;
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
@ -202,12 +238,12 @@ class DeferredFieldsTest extends TestCase
|
|||||||
');
|
');
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->queryType
|
'query' => $this->queryType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'topStories' => [
|
'topStories' => [
|
||||||
['title' => 'Story #1', 'author' => ['name' => 'John']],
|
['title' => 'Story #1', 'author' => ['name' => 'John']],
|
||||||
['title' => 'Story #3', 'author' => ['name' => 'Joe']],
|
['title' => 'Story #3', 'author' => ['name' => 'Joe']],
|
||||||
['title' => 'Story #5', 'author' => ['name' => 'John']],
|
['title' => 'Story #5', 'author' => ['name' => 'John']],
|
||||||
@ -220,9 +256,9 @@ class DeferredFieldsTest extends TestCase
|
|||||||
['title' => 'Story #4', 'author' => ['name' => 'Joe']],
|
['title' => 'Story #4', 'author' => ['name' => 'Joe']],
|
||||||
['title' => 'Story #6', 'author' => ['name' => 'Jane']],
|
['title' => 'Story #6', 'author' => ['name' => 'Jane']],
|
||||||
['title' => 'Story #8', 'author' => ['name' => 'John']],
|
['title' => 'Story #8', 'author' => ['name' => 'John']],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = Executor::execute($schema, $query);
|
$result = Executor::execute($schema, $query);
|
||||||
@ -292,12 +328,12 @@ class DeferredFieldsTest extends TestCase
|
|||||||
');
|
');
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->queryType
|
'query' => $this->queryType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
|
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
|
||||||
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']];
|
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']];
|
||||||
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
|
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
|
||||||
$author4 = ['name' => 'Dirk', 'bestFriend' => ['name' => 'John']];
|
$author4 = ['name' => 'Dirk', 'bestFriend' => ['name' => 'John']];
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
@ -306,8 +342,8 @@ class DeferredFieldsTest extends TestCase
|
|||||||
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
|
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
|
||||||
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
|
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
|
||||||
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
|
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = Executor::execute($schema, $query);
|
$result = Executor::execute($schema, $query);
|
||||||
@ -352,54 +388,56 @@ class DeferredFieldsTest extends TestCase
|
|||||||
public function testComplexRecursiveDeferredFields() : void
|
public function testComplexRecursiveDeferredFields() : void
|
||||||
{
|
{
|
||||||
$complexType = new ObjectType([
|
$complexType = new ObjectType([
|
||||||
'name' => 'ComplexType',
|
'name' => 'ComplexType',
|
||||||
'fields' => function() use (&$complexType) {
|
'fields' => function () use (&$complexType) {
|
||||||
return [
|
return [
|
||||||
'sync' => [
|
'sync' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
|
||||||
return 'sync';
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'deferred' => [
|
|
||||||
'type' => Type::string(),
|
|
||||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return new Deferred(function() use ($info) {
|
return 'sync';
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'deferred' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||||
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
|
return new Deferred(function () use ($info) {
|
||||||
$this->path[] = ['!dfd for: ', $info->path];
|
$this->path[] = ['!dfd for: ', $info->path];
|
||||||
|
|
||||||
return 'deferred';
|
return 'deferred';
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'nest' => [
|
'nest' => [
|
||||||
'type' => $complexType,
|
'type' => $complexType,
|
||||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'deferredNest' => [
|
'deferredNest' => [
|
||||||
'type' => $complexType,
|
'type' => $complexType,
|
||||||
'resolve' => function($v, $a, $c, ResolveInfo $info) {
|
'resolve' => function ($v, $a, $c, ResolveInfo $info) {
|
||||||
$this->path[] = $info->path;
|
$this->path[] = $info->path;
|
||||||
|
|
||||||
return new Deferred(function() use ($info) {
|
return new Deferred(function () use ($info) {
|
||||||
$this->path[] = ['!dfd nest for: ', $info->path];
|
$this->path[] = ['!dfd nest for: ', $info->path];
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema(['query' => $complexType]);
|
||||||
'query' => $complexType
|
|
||||||
]);
|
|
||||||
|
|
||||||
$query = Parser::parse('
|
$query = Parser::parse('
|
||||||
{
|
{
|
||||||
nest {
|
nest {
|
||||||
sync
|
sync
|
||||||
@ -427,34 +465,34 @@ class DeferredFieldsTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$result = Executor::execute($schema, $query);
|
$result = Executor::execute($schema, $query);
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'nest' => [
|
'nest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred',
|
'deferred' => 'deferred',
|
||||||
'nest' => [
|
'nest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred'
|
'deferred' => 'deferred',
|
||||||
],
|
],
|
||||||
'deferredNest' => [
|
'deferredNest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred'
|
'deferred' => 'deferred',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'deferredNest' => [
|
'deferredNest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred',
|
'deferred' => 'deferred',
|
||||||
'nest' => [
|
'nest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred'
|
'deferred' => 'deferred',
|
||||||
],
|
],
|
||||||
'deferredNest' => [
|
'deferredNest' => [
|
||||||
'sync' => 'sync',
|
'sync' => 'sync',
|
||||||
'deferred' => 'deferred'
|
'deferred' => 'deferred',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
@ -1,16 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Language\Source;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe: Execute: handles directives
|
||||||
|
*/
|
||||||
class DirectivesTest extends TestCase
|
class DirectivesTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: Execute: handles directives
|
/** @var Schema */
|
||||||
|
private static $schema;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
private static $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('basic query works')
|
* @see it('basic query works')
|
||||||
@ -20,10 +31,50 @@ class DirectivesTest extends TestCase
|
|||||||
$this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b }'));
|
$this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b }'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Source|string $doc
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
private function executeTestQuery($doc) : array
|
||||||
|
{
|
||||||
|
return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getSchema() : Schema
|
||||||
|
{
|
||||||
|
if (! self::$schema) {
|
||||||
|
self::$schema = new Schema([
|
||||||
|
'query' => new ObjectType([
|
||||||
|
'name' => 'TestType',
|
||||||
|
'fields' => [
|
||||||
|
'a' => ['type' => Type::string()],
|
||||||
|
'b' => ['type' => Type::string()],
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private static function getData() : array
|
||||||
|
{
|
||||||
|
return self::$data ?: (self::$data = [
|
||||||
|
'a' => 'a',
|
||||||
|
'b' => 'b',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function testWorksOnScalars() : void
|
public function testWorksOnScalars() : void
|
||||||
{
|
{
|
||||||
// if true includes scalar
|
// if true includes scalar
|
||||||
$this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b @include(if: true) }'));
|
$this->assertEquals(
|
||||||
|
['data' => ['a' => 'a', 'b' => 'b']],
|
||||||
|
$this->executeTestQuery('{ a, b @include(if: true) }')
|
||||||
|
);
|
||||||
|
|
||||||
// if false omits on scalar
|
// if false omits on scalar
|
||||||
$this->assertEquals(['data' => ['a' => 'a']], $this->executeTestQuery('{ a, b @include(if: false) }'));
|
$this->assertEquals(['data' => ['a' => 'a']], $this->executeTestQuery('{ a, b @include(if: false) }'));
|
||||||
@ -200,37 +251,4 @@ class DirectivesTest extends TestCase
|
|||||||
$this->executeTestQuery('{ a, b @include(if: false) @skip(if: false) }')
|
$this->executeTestQuery('{ a, b @include(if: false) @skip(if: false) }')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static $schema;
|
|
||||||
|
|
||||||
private static $data;
|
|
||||||
|
|
||||||
private static function getSchema()
|
|
||||||
{
|
|
||||||
if (!self::$schema) {
|
|
||||||
self::$schema = new Schema([
|
|
||||||
'query' => new ObjectType([
|
|
||||||
'name' => 'TestType',
|
|
||||||
'fields' => [
|
|
||||||
'a' => ['type' => Type::string()],
|
|
||||||
'b' => ['type' => Type::string()]
|
|
||||||
]
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return self::$schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getData()
|
|
||||||
{
|
|
||||||
return self::$data ?: (self::$data = [
|
|
||||||
'a' => 'a',
|
|
||||||
'b' => 'b'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeTestQuery($doc)
|
|
||||||
{
|
|
||||||
return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
|
@ -1,99 +1,119 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Executor;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Error\Warning;
|
use GraphQL\Error\Warning;
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Cat;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Dog;
|
||||||
use GraphQL\Type\Definition\CustomScalarType;
|
use GraphQL\Type\Definition\CustomScalarType;
|
||||||
|
use GraphQL\Type\Definition\EnumType;
|
||||||
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
|
use GraphQL\Type\Definition\ScalarType;
|
||||||
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\Type\Schema;
|
||||||
use PHPUnit\Framework\Error\Error;
|
use PHPUnit\Framework\Error\Error;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function count;
|
||||||
|
|
||||||
class ExecutorLazySchemaTest extends TestCase
|
class ExecutorLazySchemaTest extends TestCase
|
||||||
{
|
{
|
||||||
public $SomeScalarType;
|
/** @var ScalarType */
|
||||||
|
public $someScalarType;
|
||||||
|
|
||||||
public $SomeObjectType;
|
/** @var ObjectType */
|
||||||
|
public $someObjectType;
|
||||||
|
|
||||||
public $OtherObjectType;
|
/** @var ObjectType */
|
||||||
|
public $otherObjectType;
|
||||||
|
|
||||||
public $DeeperObjectType;
|
/** @var ObjectType */
|
||||||
|
public $deeperObjectType;
|
||||||
|
|
||||||
public $SomeUnionType;
|
/** @var UnionType */
|
||||||
|
public $someUnionType;
|
||||||
|
|
||||||
public $SomeInterfaceType;
|
/** @var InterfaceType */
|
||||||
|
public $someInterfaceType;
|
||||||
|
|
||||||
public $SomeEnumType;
|
/** @var EnumType */
|
||||||
|
public $someEnumType;
|
||||||
|
|
||||||
public $SomeInputObjectType;
|
/** @var InputObjectType */
|
||||||
|
public $someInputObjectType;
|
||||||
|
|
||||||
public $QueryType;
|
/** @var ObjectType */
|
||||||
|
public $queryType;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
public $calls = [];
|
public $calls = [];
|
||||||
|
|
||||||
|
/** @var bool[] */
|
||||||
public $loadedTypes = [];
|
public $loadedTypes = [];
|
||||||
|
|
||||||
public function testWarnsAboutSlowIsTypeOfForLazySchema() : void
|
public function testWarnsAboutSlowIsTypeOfForLazySchema() : void
|
||||||
{
|
{
|
||||||
// isTypeOf used to resolve runtime type for Interface
|
// isTypeOf used to resolve runtime type for Interface
|
||||||
$petType = new InterfaceType([
|
$petType = new InterfaceType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'fields' => function() {
|
'fields' => function () {
|
||||||
return [
|
return [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Added to interface type when defined
|
// Added to interface type when defined
|
||||||
$dogType = new ObjectType([
|
$dogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$petType],
|
'interfaces' => [$petType],
|
||||||
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
'isTypeOf' => function ($obj) {
|
||||||
'fields' => function() {
|
return $obj instanceof Dog;
|
||||||
|
},
|
||||||
|
'fields' => function () {
|
||||||
return [
|
return [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()]
|
'woofs' => ['type' => Type::boolean()],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$catType = new ObjectType([
|
$catType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$petType],
|
'interfaces' => [$petType],
|
||||||
'isTypeOf' => function ($obj) {
|
'isTypeOf' => function ($obj) {
|
||||||
return $obj instanceof Cat;
|
return $obj instanceof Cat;
|
||||||
},
|
},
|
||||||
'fields' => function() {
|
'fields' => function () {
|
||||||
return [
|
return [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()],
|
'meows' => ['type' => Type::boolean()],
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'pets' => [
|
'pets' => [
|
||||||
'type' => Type::listOf($petType),
|
'type' => Type::listOf($petType),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [$catType, $dogType],
|
'types' => [$catType, $dogType],
|
||||||
'typeLoader' => function($name) use ($dogType, $petType, $catType) {
|
'typeLoader' => function ($name) use ($dogType, $petType, $catType) {
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'Dog':
|
case 'Dog':
|
||||||
return $dogType;
|
return $dogType;
|
||||||
@ -102,7 +122,7 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
case 'Cat':
|
case 'Cat':
|
||||||
return $catType;
|
return $catType;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{
|
$query = '{
|
||||||
@ -120,7 +140,7 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
$expected = new ExecutionResult([
|
$expected = new ExecutionResult([
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['name' => 'Odie', 'woofs' => true],
|
['name' => 'Odie', 'woofs' => true],
|
||||||
['name' => 'Garfield', 'meows' => false]
|
['name' => 'Garfield', 'meows' => false],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -134,43 +154,46 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
$this->assertInstanceOf(Error::class, $result->errors[0]->getPrevious());
|
$this->assertInstanceOf(Error::class, $result->errors[0]->getPrevious());
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of '.
|
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of ' .
|
||||||
'GraphQL\Tests\Executor\Dog. Switching to slow resolution method using `isTypeOf` of all possible '.
|
'GraphQL\Tests\Executor\TestClasses\Dog. Switching to slow resolution method using `isTypeOf` of all possible ' .
|
||||||
'implementations. It requires full schema scan and degrades query performance significantly. '.
|
'implementations. It requires full schema scan and degrades query performance significantly. ' .
|
||||||
'Make sure your `resolveType` always returns valid implementation or throws.',
|
'Make sure your `resolveType` always returns valid implementation or throws.',
|
||||||
$result->errors[0]->getMessage());
|
$result->errors[0]->getMessage()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHintsOnConflictingTypeInstancesInDefinitions() : void
|
public function testHintsOnConflictingTypeInstancesInDefinitions() : void
|
||||||
{
|
{
|
||||||
$calls = [];
|
$calls = [];
|
||||||
$typeLoader = function($name) use (&$calls) {
|
$typeLoader = function ($name) use (&$calls) {
|
||||||
$calls[] = $name;
|
$calls[] = $name;
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'Test':
|
case 'Test':
|
||||||
return new ObjectType([
|
return new ObjectType([
|
||||||
'name' => 'Test',
|
'name' => 'Test',
|
||||||
'fields' => function() {
|
'fields' => function () {
|
||||||
return [
|
return [
|
||||||
'test' => Type::string(),
|
'test' => Type::string(),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => function() use ($typeLoader) {
|
'fields' => function () use ($typeLoader) {
|
||||||
return [
|
return [
|
||||||
'test' => $typeLoader('Test')
|
'test' => $typeLoader('Test'),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'typeLoader' => $typeLoader
|
'typeLoader' => $typeLoader,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '
|
$query = '
|
||||||
@ -186,8 +209,8 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
$this->assertEquals(['Test', 'Test'], $calls);
|
$this->assertEquals(['Test', 'Test'], $calls);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'Schema must contain unique named types but contains multiple types named "Test". '.
|
'Schema must contain unique named types but contains multiple types named "Test". ' .
|
||||||
'Make sure that type loader returns the same instance as defined in Query.test '.
|
'Make sure that type loader returns the same instance as defined in Query.test ' .
|
||||||
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
'(see http://webonyx.github.io/graphql-php/type-system/#type-registry).',
|
||||||
$result->errors[0]->getMessage()
|
$result->errors[0]->getMessage()
|
||||||
);
|
);
|
||||||
@ -200,54 +223,161 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
public function testSimpleQuery() : void
|
public function testSimpleQuery() : void
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->loadType('Query'),
|
'query' => $this->loadType('Query'),
|
||||||
'typeLoader' => function($name) {
|
'typeLoader' => function ($name) {
|
||||||
return $this->loadType($name, true);
|
return $this->loadType($name, true);
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{ object { string } }';
|
$query = '{ object { string } }';
|
||||||
$result = Executor::execute(
|
$result = Executor::execute(
|
||||||
$schema,
|
$schema,
|
||||||
Parser::parse($query),
|
Parser::parse($query),
|
||||||
['object' => ['string' => 'test']]
|
['object' => ['string' => 'test']]
|
||||||
);
|
);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['object' => ['string' => 'test']],
|
'data' => ['object' => ['string' => 'test']],
|
||||||
];
|
];
|
||||||
$expectedExecutorCalls = [
|
$expectedExecutorCalls = [
|
||||||
'Query.fields',
|
'Query.fields',
|
||||||
'SomeObject',
|
'SomeObject',
|
||||||
'SomeObject.fields'
|
'SomeObject.fields',
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray(true));
|
$this->assertEquals($expected, $result->toArray(true));
|
||||||
$this->assertEquals($expectedExecutorCalls, $this->calls);
|
$this->assertEquals($expectedExecutorCalls, $this->calls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function loadType($name, $isExecutorCall = false)
|
||||||
|
{
|
||||||
|
if ($isExecutorCall) {
|
||||||
|
$this->calls[] = $name;
|
||||||
|
}
|
||||||
|
$this->loadedTypes[$name] = true;
|
||||||
|
|
||||||
|
switch ($name) {
|
||||||
|
case 'Query':
|
||||||
|
return $this->queryType ?: $this->queryType = new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => function () {
|
||||||
|
$this->calls[] = 'Query.fields';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'object' => ['type' => $this->loadType('SomeObject')],
|
||||||
|
'other' => ['type' => $this->loadType('OtherObject')],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'SomeObject':
|
||||||
|
return $this->someObjectType ?: $this->someObjectType = new ObjectType([
|
||||||
|
'name' => 'SomeObject',
|
||||||
|
'fields' => function () {
|
||||||
|
$this->calls[] = 'SomeObject.fields';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'string' => ['type' => Type::string()],
|
||||||
|
'object' => ['type' => $this->someObjectType],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
'interfaces' => function () {
|
||||||
|
$this->calls[] = 'SomeObject.interfaces';
|
||||||
|
|
||||||
|
return [
|
||||||
|
$this->loadType('SomeInterface'),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'OtherObject':
|
||||||
|
return $this->otherObjectType ?: $this->otherObjectType = new ObjectType([
|
||||||
|
'name' => 'OtherObject',
|
||||||
|
'fields' => function () {
|
||||||
|
$this->calls[] = 'OtherObject.fields';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'union' => ['type' => $this->loadType('SomeUnion')],
|
||||||
|
'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'DeeperObject':
|
||||||
|
return $this->deeperObjectType ?: $this->deeperObjectType = new ObjectType([
|
||||||
|
'name' => 'DeeperObject',
|
||||||
|
'fields' => function () {
|
||||||
|
return [
|
||||||
|
'scalar' => ['type' => $this->loadType('SomeScalar')],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'SomeScalar':
|
||||||
|
return $this->someScalarType ?: $this->someScalarType = new CustomScalarType([
|
||||||
|
'name' => 'SomeScalar',
|
||||||
|
'serialize' => function ($value) {
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
'parseValue' => function ($value) {
|
||||||
|
return $value;
|
||||||
|
},
|
||||||
|
'parseLiteral' => function () {
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'SomeUnion':
|
||||||
|
return $this->someUnionType ?: $this->someUnionType = new UnionType([
|
||||||
|
'name' => 'SomeUnion',
|
||||||
|
'resolveType' => function () {
|
||||||
|
$this->calls[] = 'SomeUnion.resolveType';
|
||||||
|
|
||||||
|
return $this->loadType('DeeperObject');
|
||||||
|
},
|
||||||
|
'types' => function () {
|
||||||
|
$this->calls[] = 'SomeUnion.types';
|
||||||
|
|
||||||
|
return [$this->loadType('DeeperObject')];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
case 'SomeInterface':
|
||||||
|
return $this->someInterfaceType ?: $this->someInterfaceType = new InterfaceType([
|
||||||
|
'name' => 'SomeInterface',
|
||||||
|
'resolveType' => function () {
|
||||||
|
$this->calls[] = 'SomeInterface.resolveType';
|
||||||
|
|
||||||
|
return $this->loadType('SomeObject');
|
||||||
|
},
|
||||||
|
'fields' => function () {
|
||||||
|
$this->calls[] = 'SomeInterface.fields';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'string' => ['type' => Type::string()],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testDeepQuery() : void
|
public function testDeepQuery() : void
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->loadType('Query'),
|
'query' => $this->loadType('Query'),
|
||||||
'typeLoader' => function($name) {
|
'typeLoader' => function ($name) {
|
||||||
return $this->loadType($name, true);
|
return $this->loadType($name, true);
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '{ object { object { object { string } } } }';
|
$query = '{ object { object { object { string } } } }';
|
||||||
$result = Executor::execute(
|
$result = Executor::execute(
|
||||||
$schema,
|
$schema,
|
||||||
Parser::parse($query),
|
Parser::parse($query),
|
||||||
['object' => ['object' => ['object' => ['string' => 'test']]]]
|
['object' => ['object' => ['object' => ['string' => 'test']]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]]
|
'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]],
|
||||||
];
|
];
|
||||||
$expectedLoadedTypes = [
|
$expectedLoadedTypes = [
|
||||||
'Query' => true,
|
'Query' => true,
|
||||||
'SomeObject' => true,
|
'SomeObject' => true,
|
||||||
'OtherObject' => true
|
'OtherObject' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray(true));
|
$this->assertEquals($expected, $result->toArray(true));
|
||||||
@ -256,7 +386,7 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
$expectedExecutorCalls = [
|
$expectedExecutorCalls = [
|
||||||
'Query.fields',
|
'Query.fields',
|
||||||
'SomeObject',
|
'SomeObject',
|
||||||
'SomeObject.fields'
|
'SomeObject.fields',
|
||||||
];
|
];
|
||||||
$this->assertEquals($expectedExecutorCalls, $this->calls);
|
$this->assertEquals($expectedExecutorCalls, $this->calls);
|
||||||
}
|
}
|
||||||
@ -264,13 +394,13 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
public function testResolveUnion() : void
|
public function testResolveUnion() : void
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->loadType('Query'),
|
'query' => $this->loadType('Query'),
|
||||||
'typeLoader' => function($name) {
|
'typeLoader' => function ($name) {
|
||||||
return $this->loadType($name, true);
|
return $this->loadType($name, true);
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = '
|
$query = '
|
||||||
{
|
{
|
||||||
other {
|
other {
|
||||||
union {
|
union {
|
||||||
@ -285,17 +415,17 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
['other' => ['union' => ['scalar' => 'test']]]
|
['other' => ['union' => ['scalar' => 'test']]]
|
||||||
);
|
);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['other' => ['union' => ['scalar' => 'test']]],
|
'data' => ['other' => ['union' => ['scalar' => 'test']]],
|
||||||
];
|
];
|
||||||
$expectedLoadedTypes = [
|
$expectedLoadedTypes = [
|
||||||
'Query' => true,
|
'Query' => true,
|
||||||
'SomeObject' => true,
|
'SomeObject' => true,
|
||||||
'OtherObject' => true,
|
'OtherObject' => true,
|
||||||
'SomeUnion' => true,
|
'SomeUnion' => true,
|
||||||
'SomeInterface' => true,
|
'SomeInterface' => true,
|
||||||
'DeeperObject' => true,
|
'DeeperObject' => true,
|
||||||
'SomeScalar' => true,
|
'SomeScalar' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray(true));
|
$this->assertEquals($expected, $result->toArray(true));
|
||||||
@ -313,98 +443,4 @@ class ExecutorLazySchemaTest extends TestCase
|
|||||||
];
|
];
|
||||||
$this->assertEquals($expectedCalls, $this->calls);
|
$this->assertEquals($expectedCalls, $this->calls);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadType($name, $isExecutorCall = false)
|
|
||||||
{
|
|
||||||
if ($isExecutorCall) {
|
|
||||||
$this->calls[] = $name;
|
|
||||||
}
|
|
||||||
$this->loadedTypes[$name] = true;
|
|
||||||
|
|
||||||
switch ($name) {
|
|
||||||
case 'Query':
|
|
||||||
return $this->QueryType ?: $this->QueryType = new ObjectType([
|
|
||||||
'name' => 'Query',
|
|
||||||
'fields' => function() {
|
|
||||||
$this->calls[] = 'Query.fields';
|
|
||||||
return [
|
|
||||||
'object' => ['type' => $this->loadType('SomeObject')],
|
|
||||||
'other' => ['type' => $this->loadType('OtherObject')],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
case 'SomeObject':
|
|
||||||
return $this->SomeObjectType ?: $this->SomeObjectType = new ObjectType([
|
|
||||||
'name' => 'SomeObject',
|
|
||||||
'fields' => function() {
|
|
||||||
$this->calls[] = 'SomeObject.fields';
|
|
||||||
return [
|
|
||||||
'string' => ['type' => Type::string()],
|
|
||||||
'object' => ['type' => $this->SomeObjectType]
|
|
||||||
];
|
|
||||||
},
|
|
||||||
'interfaces' => function() {
|
|
||||||
$this->calls[] = 'SomeObject.interfaces';
|
|
||||||
return [
|
|
||||||
$this->loadType('SomeInterface')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
case 'OtherObject':
|
|
||||||
return $this->OtherObjectType ?: $this->OtherObjectType = new ObjectType([
|
|
||||||
'name' => 'OtherObject',
|
|
||||||
'fields' => function() {
|
|
||||||
$this->calls[] = 'OtherObject.fields';
|
|
||||||
return [
|
|
||||||
'union' => ['type' => $this->loadType('SomeUnion')],
|
|
||||||
'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
case 'DeeperObject':
|
|
||||||
return $this->DeeperObjectType ?: $this->DeeperObjectType = new ObjectType([
|
|
||||||
'name' => 'DeeperObject',
|
|
||||||
'fields' => function() {
|
|
||||||
return [
|
|
||||||
'scalar' => ['type' => $this->loadType('SomeScalar')],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
case 'SomeScalar';
|
|
||||||
return $this->SomeScalarType ?: $this->SomeScalarType = new CustomScalarType([
|
|
||||||
'name' => 'SomeScalar',
|
|
||||||
'serialize' => function($value) {return $value;},
|
|
||||||
'parseValue' => function($value) {return $value;},
|
|
||||||
'parseLiteral' => function() {}
|
|
||||||
]);
|
|
||||||
case 'SomeUnion':
|
|
||||||
return $this->SomeUnionType ?: $this->SomeUnionType = new UnionType([
|
|
||||||
'name' => 'SomeUnion',
|
|
||||||
'resolveType' => function() {
|
|
||||||
$this->calls[] = 'SomeUnion.resolveType';
|
|
||||||
return $this->loadType('DeeperObject');
|
|
||||||
},
|
|
||||||
'types' => function() {
|
|
||||||
$this->calls[] = 'SomeUnion.types';
|
|
||||||
return [ $this->loadType('DeeperObject') ];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
case 'SomeInterface':
|
|
||||||
return $this->SomeInterfaceType ?: $this->SomeInterfaceType = new InterfaceType([
|
|
||||||
'name' => 'SomeInterface',
|
|
||||||
'resolveType' => function() {
|
|
||||||
$this->calls[] = 'SomeInterface.resolveType';
|
|
||||||
return $this->loadType('SomeObject');
|
|
||||||
},
|
|
||||||
'fields' => function() {
|
|
||||||
$this->calls[] = 'SomeInterface.fields';
|
|
||||||
return [
|
|
||||||
'string' => ['type' => Type::string() ]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,74 +1,77 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class ExecutorSchemaTest extends TestCase
|
class ExecutorSchemaTest extends TestCase
|
||||||
{
|
{
|
||||||
// Execute: Handles execution with a complex schema
|
// Execute: Handles execution with a complex schema
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('executes using a schema')
|
* @see it('executes using a schema')
|
||||||
*/
|
*/
|
||||||
public function testExecutesUsingASchema() : void
|
public function testExecutesUsingASchema() : void
|
||||||
{
|
{
|
||||||
$BlogArticle = null;
|
$BlogArticle = null;
|
||||||
$BlogImage = new ObjectType([
|
$BlogImage = new ObjectType([
|
||||||
'name' => 'Image',
|
'name' => 'Image',
|
||||||
'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()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$BlogAuthor = new ObjectType([
|
$BlogAuthor = new ObjectType([
|
||||||
'name' => 'Author',
|
'name' => 'Author',
|
||||||
'fields' => function() use (&$BlogArticle, &$BlogImage) {
|
'fields' => function () use (&$BlogArticle, &$BlogImage) {
|
||||||
return [
|
return [
|
||||||
'id' => ['type' => Type::string()],
|
'id' => ['type' => Type::string()],
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'pic' => [
|
'pic' => [
|
||||||
'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]],
|
'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]],
|
||||||
'type' => $BlogImage,
|
'type' => $BlogImage,
|
||||||
'resolve' => function ($obj, $args) {
|
'resolve' => function ($obj, $args) {
|
||||||
return $obj['pic']($args['width'], $args['height']);
|
return $obj['pic']($args['width'], $args['height']);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'recentArticle' => $BlogArticle
|
'recentArticle' => $BlogArticle,
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$BlogArticle = new ObjectType([
|
$BlogArticle = new ObjectType([
|
||||||
'name' => 'Article',
|
'name' => 'Article',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => ['type' => Type::nonNull(Type::string())],
|
'id' => ['type' => Type::nonNull(Type::string())],
|
||||||
'isPublished' => ['type' => Type::boolean()],
|
'isPublished' => ['type' => Type::boolean()],
|
||||||
'author' => ['type' => $BlogAuthor],
|
'author' => ['type' => $BlogAuthor],
|
||||||
'title' => ['type' => Type::string()],
|
'title' => ['type' => Type::string()],
|
||||||
'body' => ['type' => Type::string()],
|
'body' => ['type' => Type::string()],
|
||||||
'keywords' => ['type' => Type::listOf(Type::string())]
|
'keywords' => ['type' => Type::listOf(Type::string())],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$BlogQuery = new ObjectType([
|
$BlogQuery = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'article' => [
|
'article' => [
|
||||||
'type' => $BlogArticle,
|
'type' => $BlogArticle,
|
||||||
'args' => ['id' => ['type' => Type::id()]],
|
'args' => ['id' => ['type' => Type::id()]],
|
||||||
'resolve' => function ($_, $args) {
|
'resolve' => function ($_, $args) {
|
||||||
return $this->article($args['id']);
|
return $this->article($args['id']);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'feed' => [
|
'feed' => [
|
||||||
'type' => Type::listOf($BlogArticle),
|
'type' => Type::listOf($BlogArticle),
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [
|
return [
|
||||||
$this->article(1),
|
$this->article(1),
|
||||||
@ -80,16 +83,15 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
$this->article(7),
|
$this->article(7),
|
||||||
$this->article(8),
|
$this->article(8),
|
||||||
$this->article(9),
|
$this->article(9),
|
||||||
$this->article(10)
|
$this->article(10),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$BlogSchema = new Schema(['query' => $BlogQuery]);
|
$BlogSchema = new Schema(['query' => $BlogQuery]);
|
||||||
|
|
||||||
|
|
||||||
$request = '
|
$request = '
|
||||||
{
|
{
|
||||||
feed {
|
feed {
|
||||||
@ -126,51 +128,71 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'feed' => [
|
'feed' => [
|
||||||
['id' => '1',
|
[
|
||||||
'title' => 'My Article 1'],
|
'id' => '1',
|
||||||
['id' => '2',
|
'title' => 'My Article 1',
|
||||||
'title' => 'My Article 2'],
|
],
|
||||||
['id' => '3',
|
[
|
||||||
'title' => 'My Article 3'],
|
'id' => '2',
|
||||||
['id' => '4',
|
'title' => 'My Article 2',
|
||||||
'title' => 'My Article 4'],
|
],
|
||||||
['id' => '5',
|
[
|
||||||
'title' => 'My Article 5'],
|
'id' => '3',
|
||||||
['id' => '6',
|
'title' => 'My Article 3',
|
||||||
'title' => 'My Article 6'],
|
],
|
||||||
['id' => '7',
|
[
|
||||||
'title' => 'My Article 7'],
|
'id' => '4',
|
||||||
['id' => '8',
|
'title' => 'My Article 4',
|
||||||
'title' => 'My Article 8'],
|
],
|
||||||
['id' => '9',
|
[
|
||||||
'title' => 'My Article 9'],
|
'id' => '5',
|
||||||
['id' => '10',
|
'title' => 'My Article 5',
|
||||||
'title' => 'My Article 10']
|
],
|
||||||
|
[
|
||||||
|
'id' => '6',
|
||||||
|
'title' => 'My Article 6',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '7',
|
||||||
|
'title' => 'My Article 7',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '8',
|
||||||
|
'title' => 'My Article 8',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '9',
|
||||||
|
'title' => 'My Article 9',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '10',
|
||||||
|
'title' => 'My Article 10',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'article' => [
|
'article' => [
|
||||||
'id' => '1',
|
'id' => '1',
|
||||||
'isPublished' => true,
|
'isPublished' => true,
|
||||||
'title' => 'My Article 1',
|
'title' => 'My Article 1',
|
||||||
'body' => 'This is a post',
|
'body' => 'This is a post',
|
||||||
'author' => [
|
'author' => [
|
||||||
'id' => '123',
|
'id' => '123',
|
||||||
'name' => 'John Smith',
|
'name' => 'John Smith',
|
||||||
'pic' => [
|
'pic' => [
|
||||||
'url' => 'cdn://123',
|
'url' => 'cdn://123',
|
||||||
'width' => 640,
|
'width' => 640,
|
||||||
'height' => 480
|
'height' => 480,
|
||||||
],
|
],
|
||||||
'recentArticle' => [
|
'recentArticle' => [
|
||||||
'id' => '1',
|
'id' => '1',
|
||||||
'isPublished' => true,
|
'isPublished' => true,
|
||||||
'title' => 'My Article 1',
|
'title' => 'My Article 1',
|
||||||
'body' => 'This is a post',
|
'body' => 'This is a post',
|
||||||
'keywords' => ['foo', 'bar', '1', 'true', null]
|
'keywords' => ['foo', 'bar', '1', 'true', null],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray());
|
$this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray());
|
||||||
@ -179,30 +201,32 @@ class ExecutorSchemaTest extends TestCase
|
|||||||
private function article($id)
|
private function article($id)
|
||||||
{
|
{
|
||||||
$johnSmith = null;
|
$johnSmith = null;
|
||||||
$article = function($id) use (&$johnSmith) {
|
$article = function ($id) use (&$johnSmith) {
|
||||||
return [
|
return [
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'isPublished' => 'true',
|
'isPublished' => 'true',
|
||||||
'author' => $johnSmith,
|
'author' => $johnSmith,
|
||||||
'title' => 'My Article ' . $id,
|
'title' => 'My Article ' . $id,
|
||||||
'body' => 'This is a post',
|
'body' => 'This is a post',
|
||||||
'hidden' => 'This data is not exposed in the schema',
|
'hidden' => 'This data is not exposed in the schema',
|
||||||
'keywords' => ['foo', 'bar', 1, true, null]
|
'keywords' => ['foo', 'bar', 1, true, null],
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$getPic = function($uid, $width, $height) {
|
$getPic = function ($uid, $width, $height) {
|
||||||
return [
|
return [
|
||||||
'url' => "cdn://$uid",
|
'url' => sprintf('cdn://%s', $uid),
|
||||||
'width' => $width,
|
'width' => $width,
|
||||||
'height' => $height
|
'height' => $height,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$johnSmith = [
|
$johnSmith = [
|
||||||
'id' => 123,
|
'id' => 123,
|
||||||
'name' => 'John Smith',
|
'name' => 'John Smith',
|
||||||
'pic' => function($width, $height) use ($getPic) {return $getPic(123, $width, $height);},
|
'pic' => function ($width, $height) use ($getPic) {
|
||||||
|
return $getPic(123, $width, $height);
|
||||||
|
},
|
||||||
'recentArticle' => $article(1),
|
'recentArticle' => $article(1),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,104 +1,34 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author: Ivo Meißner
|
* @author: Ivo Meißner
|
||||||
* Date: 03.05.16
|
* Date: 03.05.16
|
||||||
* Time: 13:14
|
* Time: 13:14
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class LazyInterfaceTest extends TestCase
|
class LazyInterfaceTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/** @var Schema */
|
||||||
* @var Schema
|
|
||||||
*/
|
|
||||||
protected $schema;
|
protected $schema;
|
||||||
|
|
||||||
/**
|
/** @var InterfaceType */
|
||||||
* @var InterfaceType
|
|
||||||
*/
|
|
||||||
protected $lazyInterface;
|
protected $lazyInterface;
|
||||||
|
|
||||||
/**
|
/** @var ObjectType */
|
||||||
* @var ObjectType
|
|
||||||
*/
|
|
||||||
protected $testObject;
|
protected $testObject;
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup schema
|
|
||||||
*/
|
|
||||||
protected function setUp()
|
|
||||||
{
|
|
||||||
$query = new ObjectType([
|
|
||||||
'name' => 'query',
|
|
||||||
'fields' => function () {
|
|
||||||
return [
|
|
||||||
'lazyInterface' => [
|
|
||||||
'type' => $this->getLazyInterfaceType(),
|
|
||||||
'resolve' => function() {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the LazyInterface
|
|
||||||
*
|
|
||||||
* @return InterfaceType
|
|
||||||
*/
|
|
||||||
protected function getLazyInterfaceType()
|
|
||||||
{
|
|
||||||
if (!$this->lazyInterface) {
|
|
||||||
$this->lazyInterface = new InterfaceType([
|
|
||||||
'name' => 'LazyInterface',
|
|
||||||
'fields' => [
|
|
||||||
'a' => Type::string()
|
|
||||||
],
|
|
||||||
'resolveType' => function() {
|
|
||||||
return $this->getTestObjectType();
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->lazyInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the test ObjectType
|
|
||||||
* @return ObjectType
|
|
||||||
*/
|
|
||||||
protected function getTestObjectType()
|
|
||||||
{
|
|
||||||
if (!$this->testObject) {
|
|
||||||
$this->testObject = new ObjectType([
|
|
||||||
'name' => 'TestObject',
|
|
||||||
'fields' => [
|
|
||||||
'name' => [
|
|
||||||
'type' => Type::string(),
|
|
||||||
'resolve' => function() {
|
|
||||||
return 'testname';
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'interfaces' => [$this->getLazyInterfaceType()]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->testObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles execution of a lazily created interface
|
* Handles execution of a lazily created interface
|
||||||
*/
|
*/
|
||||||
@ -116,12 +46,78 @@ class LazyInterfaceTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'lazyInterface' => [
|
'lazyInterface' => ['name' => 'testname'],
|
||||||
'name' => 'testname'
|
],
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup schema
|
||||||
|
*/
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$query = new ObjectType([
|
||||||
|
'name' => 'query',
|
||||||
|
'fields' => function () {
|
||||||
|
return [
|
||||||
|
'lazyInterface' => [
|
||||||
|
'type' => $this->getLazyInterfaceType(),
|
||||||
|
'resolve' => function () {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the LazyInterface
|
||||||
|
*
|
||||||
|
* @return InterfaceType
|
||||||
|
*/
|
||||||
|
protected function getLazyInterfaceType()
|
||||||
|
{
|
||||||
|
if (! $this->lazyInterface) {
|
||||||
|
$this->lazyInterface = new InterfaceType([
|
||||||
|
'name' => 'LazyInterface',
|
||||||
|
'fields' => [
|
||||||
|
'a' => Type::string(),
|
||||||
|
],
|
||||||
|
'resolveType' => function () {
|
||||||
|
return $this->getTestObjectType();
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->lazyInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the test ObjectType
|
||||||
|
* @return ObjectType
|
||||||
|
*/
|
||||||
|
protected function getTestObjectType()
|
||||||
|
{
|
||||||
|
if (! $this->testObject) {
|
||||||
|
$this->testObject = new ObjectType([
|
||||||
|
'name' => 'TestObject',
|
||||||
|
'fields' => [
|
||||||
|
'name' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'resolve' => function () {
|
||||||
|
return 'testname';
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'interfaces' => [$this->getLazyInterfaceType()],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->testObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Error\UserError;
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\SourceLocation;
|
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ListsTest extends TestCase
|
class ListsTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: Execute: Handles list nullability
|
// Describe: Execute: Handles list nullability
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [T]
|
* [T]
|
||||||
*/
|
*/
|
||||||
@ -24,23 +23,57 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
[ 1, 2 ],
|
[1, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
[ 1, null, 2 ],
|
[1, null, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
null,
|
null,
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkHandlesNullableLists($testData, $expected)
|
||||||
|
{
|
||||||
|
$testType = Type::listOf(Type::int());
|
||||||
|
$this->check($testType, $testData, $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function check($testType, $testData, $expected, $debug = false)
|
||||||
|
{
|
||||||
|
$data = ['test' => $testData];
|
||||||
|
$dataType = null;
|
||||||
|
|
||||||
|
$dataType = new ObjectType([
|
||||||
|
'name' => 'DataType',
|
||||||
|
'fields' => function () use (&$testType, &$dataType, $data) {
|
||||||
|
return [
|
||||||
|
'test' => ['type' => $testType],
|
||||||
|
'nest' => [
|
||||||
|
'type' => $dataType,
|
||||||
|
'resolve' => function () use ($data) {
|
||||||
|
return $data;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$schema = new Schema(['query' => $dataType]);
|
||||||
|
|
||||||
|
$ast = Parser::parse('{ nest { test } }');
|
||||||
|
|
||||||
|
$result = Executor::execute($schema, $ast, $data);
|
||||||
|
$this->assertArraySubset($expected, $result->toArray($debug));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [T]
|
* [T]
|
||||||
*/
|
*/
|
||||||
@ -48,26 +81,26 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1,2];
|
return [1, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, null, 2];
|
return [1, null, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rejected
|
// Rejected
|
||||||
@ -78,14 +111,14 @@ class ListsTest extends TestCase
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => ['test' => null]],
|
'data' => ['nest' => ['test' => null]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test']
|
'path' => ['nest', 'test'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -98,57 +131,64 @@ class ListsTest extends TestCase
|
|||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})],
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
],
|
||||||
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
[
|
[
|
||||||
new Deferred(function() {return 1;}),
|
new Deferred(function () {
|
||||||
new Deferred(function() {return null;}),
|
return 1;
|
||||||
new Deferred(function() {return 2;})
|
}),
|
||||||
|
new Deferred(function () {
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
new Deferred(function () {
|
||||||
|
return 2;
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains reject
|
// Contains reject
|
||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
function () {
|
function () {
|
||||||
return [
|
return [
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test', 1]
|
'path' => ['nest', 'test', 1],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,32 +200,38 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
[ 1, 2 ],
|
[1, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
[ 1, null, 2 ],
|
[1, null, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
null,
|
null,
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [['line' => 1, 'column' => 10]]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
|
||||||
|
{
|
||||||
|
$testType = Type::nonNull(Type::listOf(Type::int()));
|
||||||
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [T]!
|
* [T]!
|
||||||
*/
|
*/
|
||||||
@ -193,31 +239,31 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1,2];
|
return [1, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, null, 2];
|
return [1, null, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
null,
|
null,
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [['line' => 1, 'column' => 10]]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -225,19 +271,19 @@ class ListsTest extends TestCase
|
|||||||
// Rejected
|
// Rejected
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => null],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test']
|
'path' => ['nest', 'test'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -250,56 +296,56 @@ class ListsTest extends TestCase
|
|||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, null, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains reject
|
// Contains reject
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
function () {
|
function () {
|
||||||
return [
|
return [
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => ['test' => [1, null, 2]]],
|
'data' => ['nest' => ['test' => [1, null, 2]]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test', 1]
|
'path' => ['nest', 'test', 1],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -311,21 +357,21 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
[ 1, 2 ],
|
[1, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
[ 1, null, 2 ],
|
[1, null, 2],
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
'data' => ['nest' => ['test' => null]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -333,10 +379,16 @@ class ListsTest extends TestCase
|
|||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
null,
|
null,
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
|
||||||
|
{
|
||||||
|
$testType = Type::listOf(Type::nonNull(Type::int()));
|
||||||
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [T!]
|
* [T!]
|
||||||
*/
|
*/
|
||||||
@ -344,53 +396,53 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, 2];
|
return [1, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, null, 2];
|
return [1, null, 2];
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
'data' => ['nest' => ['test' => null]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [['line' => 1, 'column' => 10]]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rejected
|
// Rejected
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => ['test' => null]],
|
'data' => ['nest' => ['test' => null]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test']
|
'path' => ['nest', 'test'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -403,50 +455,56 @@ class ListsTest extends TestCase
|
|||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
[
|
[
|
||||||
new Deferred(function() {return 1;}),
|
new Deferred(function () {
|
||||||
new Deferred(function() {return null;}),
|
return 1;
|
||||||
new Deferred(function() {return 2;})
|
}),
|
||||||
|
new Deferred(function () {
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
new Deferred(function () {
|
||||||
|
return 2;
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => null ] ] ]
|
['data' => ['nest' => ['test' => null]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains reject
|
// Contains reject
|
||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return [
|
return [
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => ['test' => null]],
|
'data' => ['nest' => ['test' => null]],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test', 1]
|
'path' => ['nest', 'test', 1],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -458,22 +516,21 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
[ 1, 2 ],
|
[1, 2],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
[ 1, null, 2 ],
|
[1, null, 2],
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [['line' => 1, 'column' => 10 ]]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -482,18 +539,24 @@ class ListsTest extends TestCase
|
|||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
null,
|
null,
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
|
||||||
|
{
|
||||||
|
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
|
||||||
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [T!]!
|
* [T!]!
|
||||||
*/
|
*/
|
||||||
@ -501,42 +564,42 @@ class ListsTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, 2];
|
return [1, 2];
|
||||||
}),
|
}),
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return [1, null, 2];
|
return [1, null, 2];
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [ ['line' => 1, 'column' => 10] ]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -544,19 +607,19 @@ class ListsTest extends TestCase
|
|||||||
// Rejected
|
// Rejected
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test']
|
'path' => ['nest', 'test'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -569,38 +632,38 @@ class ListsTest extends TestCase
|
|||||||
// Contains values
|
// Contains values
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
|
|
||||||
],
|
],
|
||||||
[ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ]
|
['data' => ['nest' => ['test' => [1, 2]]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains null
|
// Contains null
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
[
|
[
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
'locations' => [['line' => 1, 'column' => 10]]
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
@ -609,83 +672,27 @@ class ListsTest extends TestCase
|
|||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return [
|
return [
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
throw new UserError('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function () {
|
||||||
return 2;
|
return 2;
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
'data' => ['nest' => null ],
|
'data' => ['nest' => null],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'bad',
|
'message' => 'bad',
|
||||||
'locations' => [['line' => 1, 'column' => 10]],
|
'locations' => [['line' => 1, 'column' => 10]],
|
||||||
'path' => ['nest', 'test']
|
'path' => ['nest', 'test'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkHandlesNullableLists($testData, $expected)
|
|
||||||
{
|
|
||||||
$testType = Type::listOf(Type::int());
|
|
||||||
$this->check($testType, $testData, $expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
|
|
||||||
{
|
|
||||||
$testType = Type::nonNull(Type::listOf(Type::int()));
|
|
||||||
$this->check($testType, $testData, $expected, $debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
|
|
||||||
{
|
|
||||||
$testType = Type::listOf(Type::nonNull(Type::int()));
|
|
||||||
$this->check($testType, $testData, $expected, $debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
|
|
||||||
{
|
|
||||||
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
|
|
||||||
$this->check($testType, $testData, $expected, $debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function check($testType, $testData, $expected, $debug = false)
|
|
||||||
{
|
|
||||||
$data = ['test' => $testData];
|
|
||||||
$dataType = null;
|
|
||||||
|
|
||||||
$dataType = new ObjectType([
|
|
||||||
'name' => 'DataType',
|
|
||||||
'fields' => function () use (&$testType, &$dataType, $data) {
|
|
||||||
return [
|
|
||||||
'test' => [
|
|
||||||
'type' => $testType
|
|
||||||
],
|
|
||||||
'nest' => [
|
|
||||||
'type' => $dataType,
|
|
||||||
'resolve' => function () use ($data) {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
$schema = new Schema([
|
|
||||||
'query' => $dataType
|
|
||||||
]);
|
|
||||||
|
|
||||||
$ast = Parser::parse('{ nest { test } }');
|
|
||||||
|
|
||||||
$result = Executor::execute($schema, $ast, $data);
|
|
||||||
$this->assertArraySubset($expected, $result->toArray($debug));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Tests\Executor\TestClasses\Root;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class MutationsTest extends TestCase
|
class MutationsTest extends TestCase
|
||||||
{
|
{
|
||||||
// Execute: Handles mutation execution ordering
|
// Execute: Handles mutation execution ordering
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('evaluates mutations serially')
|
* @see it('evaluates mutations serially')
|
||||||
*/
|
*/
|
||||||
public function testEvaluatesMutationsSerially() : void
|
public function testEvaluatesMutationsSerially() : void
|
||||||
{
|
{
|
||||||
$doc = 'mutation M {
|
$doc = 'mutation M {
|
||||||
first: immediatelyChangeTheNumber(newNumber: 1) {
|
first: immediatelyChangeTheNumber(newNumber: 1) {
|
||||||
theNumber
|
theNumber
|
||||||
},
|
},
|
||||||
@ -38,36 +37,79 @@ class MutationsTest extends TestCase
|
|||||||
theNumber
|
theNumber
|
||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'first' => [
|
'first' => ['theNumber' => 1],
|
||||||
'theNumber' => 1
|
'second' => ['theNumber' => 2],
|
||||||
],
|
'third' => ['theNumber' => 3],
|
||||||
'second' => [
|
'fourth' => ['theNumber' => 4],
|
||||||
'theNumber' => 2
|
'fifth' => ['theNumber' => 5],
|
||||||
],
|
],
|
||||||
'third' => [
|
|
||||||
'theNumber' => 3
|
|
||||||
],
|
|
||||||
'fourth' => [
|
|
||||||
'theNumber' => 4
|
|
||||||
],
|
|
||||||
'fifth' => [
|
|
||||||
'theNumber' => 5
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $mutationResult->toArray());
|
$this->assertEquals($expected, $mutationResult->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function schema() : Schema
|
||||||
|
{
|
||||||
|
$numberHolderType = new ObjectType([
|
||||||
|
'fields' => [
|
||||||
|
'theNumber' => ['type' => Type::int()],
|
||||||
|
],
|
||||||
|
'name' => 'NumberHolder',
|
||||||
|
]);
|
||||||
|
$schema = new Schema([
|
||||||
|
'query' => new ObjectType([
|
||||||
|
'fields' => [
|
||||||
|
'numberHolder' => ['type' => $numberHolderType],
|
||||||
|
],
|
||||||
|
'name' => 'Query',
|
||||||
|
]),
|
||||||
|
'mutation' => new ObjectType([
|
||||||
|
'fields' => [
|
||||||
|
'immediatelyChangeTheNumber' => [
|
||||||
|
'type' => $numberHolderType,
|
||||||
|
'args' => ['newNumber' => ['type' => Type::int()]],
|
||||||
|
'resolve' => function (Root $obj, $args) {
|
||||||
|
return $obj->immediatelyChangeTheNumber($args['newNumber']);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'promiseToChangeTheNumber' => [
|
||||||
|
'type' => $numberHolderType,
|
||||||
|
'args' => ['newNumber' => ['type' => Type::int()]],
|
||||||
|
'resolve' => function (Root $obj, $args) {
|
||||||
|
return $obj->promiseToChangeTheNumber($args['newNumber']);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'failToChangeTheNumber' => [
|
||||||
|
'type' => $numberHolderType,
|
||||||
|
'args' => ['newNumber' => ['type' => Type::int()]],
|
||||||
|
'resolve' => function (Root $obj, $args) {
|
||||||
|
$obj->failToChangeTheNumber();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'promiseAndFailToChangeTheNumber' => [
|
||||||
|
'type' => $numberHolderType,
|
||||||
|
'args' => ['newNumber' => ['type' => Type::int()]],
|
||||||
|
'resolve' => function (Root $obj, $args) {
|
||||||
|
return $obj->promiseAndFailToChangeTheNumber();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'name' => 'Mutation',
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('evaluates mutations correctly in the presense of a failed mutation')
|
* @see it('evaluates mutations correctly in the presense of a failed mutation')
|
||||||
*/
|
*/
|
||||||
public function testEvaluatesMutationsCorrectlyInThePresenseOfAFailedMutation() : void
|
public function testEvaluatesMutationsCorrectlyInThePresenseOfAFailedMutation() : void
|
||||||
{
|
{
|
||||||
$doc = 'mutation M {
|
$doc = 'mutation M {
|
||||||
first: immediatelyChangeTheNumber(newNumber: 1) {
|
first: immediatelyChangeTheNumber(newNumber: 1) {
|
||||||
theNumber
|
theNumber
|
||||||
},
|
},
|
||||||
@ -87,147 +129,28 @@ class MutationsTest extends TestCase
|
|||||||
theNumber
|
theNumber
|
||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'first' => [
|
'first' => ['theNumber' => 1],
|
||||||
'theNumber' => 1
|
'second' => ['theNumber' => 2],
|
||||||
],
|
'third' => null,
|
||||||
'second' => [
|
'fourth' => ['theNumber' => 4],
|
||||||
'theNumber' => 2
|
'fifth' => ['theNumber' => 5],
|
||||||
],
|
'sixth' => null,
|
||||||
'third' => null,
|
|
||||||
'fourth' => [
|
|
||||||
'theNumber' => 4
|
|
||||||
],
|
|
||||||
'fifth' => [
|
|
||||||
'theNumber' => 5
|
|
||||||
],
|
|
||||||
'sixth' => null,
|
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot change the number',
|
'debugMessage' => 'Cannot change the number',
|
||||||
'locations' => [['line' => 8, 'column' => 7]]
|
'locations' => [['line' => 8, 'column' => 7]],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot change the number',
|
'debugMessage' => 'Cannot change the number',
|
||||||
'locations' => [['line' => 17, 'column' => 7]]
|
'locations' => [['line' => 17, 'column' => 7]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, $mutationResult->toArray(true));
|
$this->assertArraySubset($expected, $mutationResult->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function schema()
|
|
||||||
{
|
|
||||||
$numberHolderType = new ObjectType([
|
|
||||||
'fields' => [
|
|
||||||
'theNumber' => ['type' => Type::int()],
|
|
||||||
],
|
|
||||||
'name' => 'NumberHolder',
|
|
||||||
]);
|
|
||||||
$schema = new Schema([
|
|
||||||
'query' => new ObjectType([
|
|
||||||
'fields' => [
|
|
||||||
'numberHolder' => ['type' => $numberHolderType],
|
|
||||||
],
|
|
||||||
'name' => 'Query',
|
|
||||||
]),
|
|
||||||
'mutation' => new ObjectType([
|
|
||||||
'fields' => [
|
|
||||||
'immediatelyChangeTheNumber' => [
|
|
||||||
'type' => $numberHolderType,
|
|
||||||
'args' => ['newNumber' => ['type' => Type::int()]],
|
|
||||||
'resolve' => (function (Root $obj, $args) {
|
|
||||||
return $obj->immediatelyChangeTheNumber($args['newNumber']);
|
|
||||||
})
|
|
||||||
],
|
|
||||||
'promiseToChangeTheNumber' => [
|
|
||||||
'type' => $numberHolderType,
|
|
||||||
'args' => ['newNumber' => ['type' => Type::int()]],
|
|
||||||
'resolve' => (function (Root $obj, $args) {
|
|
||||||
return $obj->promiseToChangeTheNumber($args['newNumber']);
|
|
||||||
})
|
|
||||||
],
|
|
||||||
'failToChangeTheNumber' => [
|
|
||||||
'type' => $numberHolderType,
|
|
||||||
'args' => ['newNumber' => ['type' => Type::int()]],
|
|
||||||
'resolve' => (function (Root $obj, $args) {
|
|
||||||
return $obj->failToChangeTheNumber($args['newNumber']);
|
|
||||||
})
|
|
||||||
],
|
|
||||||
'promiseAndFailToChangeTheNumber' => [
|
|
||||||
'type' => $numberHolderType,
|
|
||||||
'args' => ['newNumber' => ['type' => Type::int()]],
|
|
||||||
'resolve' => (function (Root $obj, $args) {
|
|
||||||
return $obj->promiseAndFailToChangeTheNumber($args['newNumber']);
|
|
||||||
})
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'name' => 'Mutation',
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
return $schema;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NumberHolder
|
|
||||||
{
|
|
||||||
public $theNumber;
|
|
||||||
|
|
||||||
public function __construct($originalNumber)
|
|
||||||
{
|
|
||||||
$this->theNumber = $originalNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Root {
|
|
||||||
public $numberHolder;
|
|
||||||
|
|
||||||
public function __construct($originalNumber)
|
|
||||||
{
|
|
||||||
$this->numberHolder = new NumberHolder($originalNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $newNumber
|
|
||||||
* @return NumberHolder
|
|
||||||
*/
|
|
||||||
public function immediatelyChangeTheNumber($newNumber)
|
|
||||||
{
|
|
||||||
$this->numberHolder->theNumber = $newNumber;
|
|
||||||
return $this->numberHolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $newNumber
|
|
||||||
*
|
|
||||||
* @return Deferred
|
|
||||||
*/
|
|
||||||
public function promiseToChangeTheNumber($newNumber)
|
|
||||||
{
|
|
||||||
return new Deferred(function () use ($newNumber) {
|
|
||||||
return $this->immediatelyChangeTheNumber($newNumber);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function failToChangeTheNumber()
|
|
||||||
{
|
|
||||||
throw new \Exception('Cannot change the number');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Deferred
|
|
||||||
*/
|
|
||||||
public function promiseAndFailToChangeTheNumber()
|
|
||||||
{
|
|
||||||
return new Deferred(function () {
|
|
||||||
throw new \Exception("Cannot change the number");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
|
use GraphQL\Error\FormattedError;
|
||||||
use GraphQL\Error\UserError;
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Language\SourceLocation;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class NonNullTest extends TestCase
|
class NonNullTest extends TestCase
|
||||||
@ -27,41 +29,46 @@ class NonNullTest extends TestCase
|
|||||||
/** @var \Exception */
|
/** @var \Exception */
|
||||||
public $promiseNonNullError;
|
public $promiseNonNullError;
|
||||||
|
|
||||||
|
/** @var callable[] */
|
||||||
public $throwingData;
|
public $throwingData;
|
||||||
|
|
||||||
|
/** @var callable[] */
|
||||||
public $nullingData;
|
public $nullingData;
|
||||||
|
|
||||||
|
/** @var Schema */
|
||||||
public $schema;
|
public $schema;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->syncError = new UserError('sync');
|
$this->syncError = new UserError('sync');
|
||||||
$this->syncNonNullError = new UserError('syncNonNull');
|
$this->syncNonNullError = new UserError('syncNonNull');
|
||||||
$this->promiseError = new UserError('promise');
|
$this->promiseError = new UserError('promise');
|
||||||
$this->promiseNonNullError = new UserError('promiseNonNull');
|
$this->promiseNonNullError = new UserError('promiseNonNull');
|
||||||
|
|
||||||
$this->throwingData = [
|
$this->throwingData = [
|
||||||
'sync' => function () {
|
'sync' => function () {
|
||||||
throw $this->syncError;
|
throw $this->syncError;
|
||||||
},
|
},
|
||||||
'syncNonNull' => function () {
|
'syncNonNull' => function () {
|
||||||
throw $this->syncNonNullError;
|
throw $this->syncNonNullError;
|
||||||
},
|
},
|
||||||
'promise' => function () {
|
'promise' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw $this->promiseError;
|
throw $this->promiseError;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'promiseNonNull' => function () {
|
'promiseNonNull' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw $this->promiseNonNullError;
|
throw $this->promiseNonNullError;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'syncNest' => function () {
|
'syncNest' => function () {
|
||||||
return $this->throwingData;
|
return $this->throwingData;
|
||||||
},
|
},
|
||||||
'syncNonNullNest' => function () {
|
'syncNonNullNest' => function () {
|
||||||
return $this->throwingData;
|
return $this->throwingData;
|
||||||
},
|
},
|
||||||
'promiseNest' => function () {
|
'promiseNest' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
return $this->throwingData;
|
return $this->throwingData;
|
||||||
});
|
});
|
||||||
@ -74,29 +81,29 @@ class NonNullTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
$this->nullingData = [
|
$this->nullingData = [
|
||||||
'sync' => function () {
|
'sync' => function () {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'syncNonNull' => function () {
|
'syncNonNull' => function () {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'promise' => function () {
|
'promise' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'promiseNonNull' => function () {
|
'promiseNonNull' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'syncNest' => function () {
|
'syncNest' => function () {
|
||||||
return $this->nullingData;
|
return $this->nullingData;
|
||||||
},
|
},
|
||||||
'syncNonNullNest' => function () {
|
'syncNonNullNest' => function () {
|
||||||
return $this->nullingData;
|
return $this->nullingData;
|
||||||
},
|
},
|
||||||
'promiseNest' => function () {
|
'promiseNest' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
return $this->nullingData;
|
return $this->nullingData;
|
||||||
});
|
});
|
||||||
@ -109,19 +116,19 @@ class NonNullTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
$dataType = new ObjectType([
|
$dataType = new ObjectType([
|
||||||
'name' => 'DataType',
|
'name' => 'DataType',
|
||||||
'fields' => function() use (&$dataType) {
|
'fields' => function () use (&$dataType) {
|
||||||
return [
|
return [
|
||||||
'sync' => ['type' => Type::string()],
|
'sync' => ['type' => Type::string()],
|
||||||
'syncNonNull' => ['type' => Type::nonNull(Type::string())],
|
'syncNonNull' => ['type' => Type::nonNull(Type::string())],
|
||||||
'promise' => Type::string(),
|
'promise' => Type::string(),
|
||||||
'promiseNonNull' => Type::nonNull(Type::string()),
|
'promiseNonNull' => Type::nonNull(Type::string()),
|
||||||
'syncNest' => $dataType,
|
'syncNest' => $dataType,
|
||||||
'syncNonNullNest' => Type::nonNull($dataType),
|
'syncNonNullNest' => Type::nonNull($dataType),
|
||||||
'promiseNest' => $dataType,
|
'promiseNest' => $dataType,
|
||||||
'promiseNonNullNest' => Type::nonNull($dataType),
|
'promiseNonNullNest' => Type::nonNull($dataType),
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->schema = new Schema(['query' => $dataType]);
|
$this->schema = new Schema(['query' => $dataType]);
|
||||||
@ -143,17 +150,18 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['sync' => null],
|
||||||
'sync' => null,
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
FormattedError::create(
|
||||||
$this->syncError->getMessage(),
|
$this->syncError->getMessage(),
|
||||||
[new SourceLocation(3, 9)]
|
[new SourceLocation(3, 9)]
|
||||||
)
|
),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsANullableFieldThatThrowsInAPromise() : void
|
public function testNullsANullableFieldThatThrowsInAPromise() : void
|
||||||
@ -167,18 +175,19 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promise' => null],
|
||||||
'promise' => null,
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
FormattedError::create(
|
||||||
$this->promiseError->getMessage(),
|
$this->promiseError->getMessage(),
|
||||||
[new SourceLocation(3, 9)]
|
[new SourceLocation(3, 9)]
|
||||||
)
|
),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() : void
|
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() : void
|
||||||
@ -195,14 +204,15 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['syncNest' => null],
|
||||||
'syncNest' => null
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)])
|
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() : void
|
public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() : void
|
||||||
@ -218,15 +228,16 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['syncNest' => null],
|
||||||
'syncNest' => null
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)])
|
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() : void
|
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() : void
|
||||||
@ -242,15 +253,16 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promiseNest' => null],
|
||||||
'promiseNest' => null
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)])
|
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() : void
|
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() : void
|
||||||
@ -266,15 +278,16 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promiseNest' => null],
|
||||||
'promiseNest' => null
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)])
|
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,28 +327,28 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -353,10 +366,13 @@ class NonNullTest extends TestCase
|
|||||||
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]),
|
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]),
|
||||||
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]),
|
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]),
|
||||||
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]),
|
FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() : void
|
public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() : void
|
||||||
@ -413,10 +429,10 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'syncNest' => null,
|
'syncNest' => null,
|
||||||
'promiseNest' => null,
|
'promiseNest' => null,
|
||||||
'anotherNest' => null,
|
'anotherNest' => null,
|
||||||
'anotherPromiseNest' => null,
|
'anotherPromiseNest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
@ -424,10 +440,13 @@ class NonNullTest extends TestCase
|
|||||||
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(19, 19)]),
|
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(19, 19)]),
|
||||||
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(30, 19)]),
|
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(30, 19)]),
|
||||||
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(41, 19)]),
|
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(41, 19)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsANullableFieldThatSynchronouslyReturnsNull() : void
|
public function testNullsANullableFieldThatSynchronouslyReturnsNull() : void
|
||||||
@ -441,11 +460,12 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['sync' => null],
|
||||||
'sync' => null,
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsANullableFieldThatReturnsNullInAPromise() : void
|
public function testNullsANullableFieldThatReturnsNullInAPromise() : void
|
||||||
@ -459,12 +479,13 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promise' => null],
|
||||||
'promise' => null,
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() : void
|
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() : void
|
||||||
@ -480,17 +501,18 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['syncNest' => null],
|
||||||
'syncNest' => null
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
||||||
'locations' => [['line' => 4, 'column' => 11]]
|
'locations' => [['line' => 4, 'column' => 11]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true));
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() : void
|
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() : void
|
||||||
@ -506,15 +528,13 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['syncNest' => null],
|
||||||
'syncNest' => null,
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||||
'locations' => [['line' => 4, 'column' => 11]]
|
'locations' => [['line' => 4, 'column' => 11]],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
@ -536,15 +556,13 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promiseNest' => null],
|
||||||
'promiseNest' => null,
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
||||||
'locations' => [['line' => 4, 'column' => 11]]
|
'locations' => [['line' => 4, 'column' => 11]],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
@ -566,15 +584,13 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['promiseNest' => null],
|
||||||
'promiseNest' => null,
|
|
||||||
],
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||||
'locations' => [['line' => 4, 'column' => 11]]
|
'locations' => [['line' => 4, 'column' => 11]],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
@ -618,31 +634,31 @@ class NonNullTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
'syncNest' => [
|
'syncNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
],
|
],
|
||||||
'promiseNest' => [
|
'promiseNest' => [
|
||||||
'sync' => null,
|
'sync' => null,
|
||||||
'promise' => null,
|
'promise' => null,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray();
|
$actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray();
|
||||||
@ -703,18 +719,18 @@ class NonNullTest extends TestCase
|
|||||||
$ast = Parser::parse($doc);
|
$ast = Parser::parse($doc);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'syncNest' => null,
|
'syncNest' => null,
|
||||||
'promiseNest' => null,
|
'promiseNest' => null,
|
||||||
'anotherNest' => null,
|
'anotherNest' => null,
|
||||||
'anotherPromiseNest' => null,
|
'anotherPromiseNest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [ ['line' => 8, 'column' => 19]]],
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 8, 'column' => 19]]],
|
||||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [ ['line' => 19, 'column' => 19]]],
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 19, 'column' => 19]]],
|
||||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [ ['line' => 30, 'column' => 19]]],
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 30, 'column' => 19]]],
|
||||||
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [ ['line' => 41, 'column' => 19]]],
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 41, 'column' => 19]]],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
@ -734,10 +750,10 @@ class NonNullTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)])
|
FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray();
|
$actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray();
|
||||||
$this->assertArraySubset($expected, $actual);
|
$this->assertArraySubset($expected, $actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,10 +768,13 @@ class NonNullTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(2, 17)]),
|
FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(2, 17)]),
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() : void
|
public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() : void
|
||||||
@ -769,9 +788,9 @@ class NonNullTest extends TestCase
|
|||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.',
|
||||||
'locations' => [['line' => 2, 'column' => 17]]
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
$expected,
|
$expected,
|
||||||
@ -791,9 +810,9 @@ class NonNullTest extends TestCase
|
|||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.',
|
||||||
'locations' => [['line' => 2, 'column' => 17]]
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor\Promise;
|
namespace GraphQL\Tests\Executor\Promise;
|
||||||
|
|
||||||
|
|
||||||
use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter;
|
use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter;
|
||||||
use GraphQL\Executor\Promise\Promise;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use React\Promise\Deferred;
|
use React\Promise\Deferred;
|
||||||
use React\Promise\FulfilledPromise;
|
use React\Promise\FulfilledPromise;
|
||||||
use React\Promise\LazyPromise;
|
use React\Promise\LazyPromise;
|
||||||
use React\Promise\Promise as ReactPromise;
|
use React\Promise\Promise as ReactPromise;
|
||||||
use React\Promise\RejectedPromise;
|
use React\Promise\RejectedPromise;
|
||||||
|
use function class_exists;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group ReactPromise
|
* @group ReactPromise
|
||||||
@ -20,27 +20,35 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
{
|
{
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
if(! class_exists('React\Promise\Promise')) {
|
if (class_exists('React\Promise\Promise')) {
|
||||||
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void
|
public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
|
|
||||||
$this->assertSame(true, $reactAdapter->isThenable(new ReactPromise(function() {})));
|
$this->assertTrue(
|
||||||
$this->assertSame(true, $reactAdapter->isThenable(new FulfilledPromise()));
|
$reactAdapter->isThenable(new ReactPromise(function () {
|
||||||
$this->assertSame(true, $reactAdapter->isThenable(new RejectedPromise()));
|
}))
|
||||||
$this->assertSame(true, $reactAdapter->isThenable(new LazyPromise(function() {})));
|
);
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(false));
|
$this->assertTrue($reactAdapter->isThenable(new FulfilledPromise()));
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(true));
|
$this->assertTrue($reactAdapter->isThenable(new RejectedPromise()));
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(1));
|
$this->assertTrue(
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(0));
|
$reactAdapter->isThenable(new LazyPromise(function () {
|
||||||
$this->assertSame(false, $reactAdapter->isThenable('test'));
|
}))
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(''));
|
);
|
||||||
$this->assertSame(false, $reactAdapter->isThenable([]));
|
$this->assertFalse($reactAdapter->isThenable(false));
|
||||||
$this->assertSame(false, $reactAdapter->isThenable(new \stdClass()));
|
$this->assertFalse($reactAdapter->isThenable(true));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable(1));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable(0));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable('test'));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable(''));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable([]));
|
||||||
|
$this->assertFalse($reactAdapter->isThenable(new \stdClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConvertsReactPromisesToGraphQlOnes() : void
|
public function testConvertsReactPromisesToGraphQlOnes() : void
|
||||||
@ -58,13 +66,16 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$reactPromise = new FulfilledPromise(1);
|
$reactPromise = new FulfilledPromise(1);
|
||||||
$promise = $reactAdapter->convertThenable($reactPromise);
|
$promise = $reactAdapter->convertThenable($reactPromise);
|
||||||
|
|
||||||
$result = null;
|
$result = null;
|
||||||
|
|
||||||
$resultPromise = $reactAdapter->then($promise, function ($value) use (&$result) {
|
$resultPromise = $reactAdapter->then(
|
||||||
$result = $value;
|
$promise,
|
||||||
});
|
function ($value) use (&$result) {
|
||||||
|
$result = $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertSame(1, $result);
|
$this->assertSame(1, $result);
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise);
|
||||||
@ -73,9 +84,9 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testCreate() : void
|
public function testCreate() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$resolvedPromise = $reactAdapter->create(function ($resolve) {
|
$resolvedPromise = $reactAdapter->create(function ($resolve) {
|
||||||
$resolve(1);
|
$resolve(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise);
|
||||||
@ -84,7 +95,7 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
$result = null;
|
$result = null;
|
||||||
|
|
||||||
$resolvedPromise->then(function ($value) use (&$result) {
|
$resolvedPromise->then(function ($value) use (&$result) {
|
||||||
$result = $value;
|
$result = $value;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertSame(1, $result);
|
$this->assertSame(1, $result);
|
||||||
@ -92,7 +103,7 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testCreateFulfilled() : void
|
public function testCreateFulfilled() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$fulfilledPromise = $reactAdapter->createFulfilled(1);
|
$fulfilledPromise = $reactAdapter->createFulfilled(1);
|
||||||
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $fulfilledPromise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $fulfilledPromise);
|
||||||
@ -109,7 +120,7 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testCreateRejected() : void
|
public function testCreateRejected() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise'));
|
$rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise'));
|
||||||
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $rejectedPromise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $rejectedPromise);
|
||||||
@ -117,9 +128,12 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
$exception = null;
|
$exception = null;
|
||||||
|
|
||||||
$rejectedPromise->then(null, function ($error) use (&$exception) {
|
$rejectedPromise->then(
|
||||||
$exception = $error;
|
null,
|
||||||
});
|
function ($error) use (&$exception) {
|
||||||
|
$exception = $error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertInstanceOf('\Exception', $exception);
|
$this->assertInstanceOf('\Exception', $exception);
|
||||||
$this->assertEquals('I am a bad promise', $exception->getMessage());
|
$this->assertEquals('I am a bad promise', $exception->getMessage());
|
||||||
@ -128,7 +142,7 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
public function testAll() : void
|
public function testAll() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)];
|
$promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)];
|
||||||
|
|
||||||
$allPromise = $reactAdapter->all($promises);
|
$allPromise = $reactAdapter->all($promises);
|
||||||
|
|
||||||
@ -138,7 +152,7 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
$result = null;
|
$result = null;
|
||||||
|
|
||||||
$allPromise->then(function ($values) use (&$result) {
|
$allPromise->then(function ($values) use (&$result) {
|
||||||
$result = $values;
|
$result = $values;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertSame([1, 2, 3], $result);
|
$this->assertSame([1, 2, 3], $result);
|
||||||
@ -147,9 +161,9 @@ class ReactPromiseAdapterTest extends TestCase
|
|||||||
public function testAllShouldPreserveTheOrderOfTheArrayWhenResolvingAsyncPromises() : void
|
public function testAllShouldPreserveTheOrderOfTheArrayWhenResolvingAsyncPromises() : void
|
||||||
{
|
{
|
||||||
$reactAdapter = new ReactPromiseAdapter();
|
$reactAdapter = new ReactPromiseAdapter();
|
||||||
$deferred = new Deferred();
|
$deferred = new Deferred();
|
||||||
$promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)];
|
$promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)];
|
||||||
$result = null;
|
$result = null;
|
||||||
|
|
||||||
$reactAdapter->all($promises)->then(function ($values) use (&$result) {
|
$reactAdapter->all($promises)->then(function ($values) use (&$result) {
|
||||||
$result = $values;
|
$result = $values;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor\Promise;
|
namespace GraphQL\Tests\Executor\Promise;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
@ -10,9 +13,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class SyncPromiseAdapterTest extends TestCase
|
class SyncPromiseAdapterTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/** @var SyncPromiseAdapter */
|
||||||
* @var SyncPromiseAdapter
|
|
||||||
*/
|
|
||||||
private $promises;
|
private $promises;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
@ -22,7 +23,11 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testIsThenable() : void
|
public function testIsThenable() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals(true, $this->promises->isThenable(new Deferred(function() {})));
|
$this->assertEquals(
|
||||||
|
true,
|
||||||
|
$this->promises->isThenable(new Deferred(function () {
|
||||||
|
}))
|
||||||
|
);
|
||||||
$this->assertEquals(false, $this->promises->isThenable(false));
|
$this->assertEquals(false, $this->promises->isThenable(false));
|
||||||
$this->assertEquals(false, $this->promises->isThenable(true));
|
$this->assertEquals(false, $this->promises->isThenable(true));
|
||||||
$this->assertEquals(false, $this->promises->isThenable(1));
|
$this->assertEquals(false, $this->promises->isThenable(1));
|
||||||
@ -35,7 +40,8 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testConvert() : void
|
public function testConvert() : void
|
||||||
{
|
{
|
||||||
$dfd = new Deferred(function() {});
|
$dfd = new Deferred(function () {
|
||||||
|
});
|
||||||
$result = $this->promises->convertThenable($dfd);
|
$result = $this->promises->convertThenable($dfd);
|
||||||
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result);
|
||||||
@ -48,7 +54,8 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testThen() : void
|
public function testThen() : void
|
||||||
{
|
{
|
||||||
$dfd = new Deferred(function() {});
|
$dfd = new Deferred(function () {
|
||||||
|
});
|
||||||
$promise = $this->promises->convertThenable($dfd);
|
$promise = $this->promises->convertThenable($dfd);
|
||||||
|
|
||||||
$result = $this->promises->then($promise);
|
$result = $this->promises->then($promise);
|
||||||
@ -59,18 +66,55 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
|
|
||||||
public function testCreatePromise() : void
|
public function testCreatePromise() : void
|
||||||
{
|
{
|
||||||
$promise = $this->promises->create(function($resolve, $reject) {});
|
$promise = $this->promises->create(function ($resolve, $reject) {
|
||||||
|
});
|
||||||
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
|
||||||
|
|
||||||
$promise = $this->promises->create(function($resolve, $reject) {
|
$promise = $this->promises->create(function ($resolve, $reject) {
|
||||||
$resolve('A');
|
$resolve('A');
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertValidPromise($promise, null, 'A', SyncPromise::FULFILLED);
|
$this->assertValidPromise($promise, null, 'A', SyncPromise::FULFILLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
|
||||||
|
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
|
||||||
|
|
||||||
|
$actualNextValue = null;
|
||||||
|
$actualNextReason = null;
|
||||||
|
$onFulfilledCalled = false;
|
||||||
|
$onRejectedCalled = false;
|
||||||
|
|
||||||
|
$promise->then(
|
||||||
|
function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
|
||||||
|
$onFulfilledCalled = true;
|
||||||
|
$actualNextValue = $nextValue;
|
||||||
|
},
|
||||||
|
function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) {
|
||||||
|
$onRejectedCalled = true;
|
||||||
|
$actualNextReason = $reason->getMessage();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame($onFulfilledCalled, false);
|
||||||
|
$this->assertSame($onRejectedCalled, false);
|
||||||
|
|
||||||
|
SyncPromise::runQueue();
|
||||||
|
|
||||||
|
if ($expectedNextState !== SyncPromise::PENDING) {
|
||||||
|
$this->assertSame(! $expectedNextReason, $onFulfilledCalled);
|
||||||
|
$this->assertSame(! ! $expectedNextReason, $onRejectedCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame($expectedNextValue, $actualNextValue);
|
||||||
|
$this->assertSame($expectedNextReason, $actualNextReason);
|
||||||
|
$this->assertSame($expectedNextState, $promise->adoptedPromise->state);
|
||||||
|
}
|
||||||
|
|
||||||
public function testCreateFulfilledPromise() : void
|
public function testCreateFulfilledPromise() : void
|
||||||
{
|
{
|
||||||
$promise = $this->promises->createFulfilled('test');
|
$promise = $this->promises->createFulfilled('test');
|
||||||
@ -94,8 +138,8 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
$promise1 = new SyncPromise();
|
$promise1 = new SyncPromise();
|
||||||
$promise2 = new SyncPromise();
|
$promise2 = new SyncPromise();
|
||||||
$promise3 = $promise2->then(
|
$promise3 = $promise2->then(
|
||||||
function($value) {
|
function ($value) {
|
||||||
return $value .'-value3';
|
return $value . '-value3';
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -105,7 +149,7 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
new Promise($promise2, $this->promises),
|
new Promise($promise2, $this->promises),
|
||||||
3,
|
3,
|
||||||
new Promise($promise3, $this->promises),
|
new Promise($promise3, $this->promises),
|
||||||
[]
|
[],
|
||||||
];
|
];
|
||||||
|
|
||||||
$promise = $this->promises->all($data);
|
$promise = $this->promises->all($data);
|
||||||
@ -114,36 +158,46 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
$promise1->resolve('value1');
|
$promise1->resolve('value1');
|
||||||
$this->assertValidPromise($promise, null, null, SyncPromise::PENDING);
|
$this->assertValidPromise($promise, null, null, SyncPromise::PENDING);
|
||||||
$promise2->resolve('value2');
|
$promise2->resolve('value2');
|
||||||
$this->assertValidPromise($promise, null, ['1', 'value1', 'value2', 3, 'value2-value3', []], SyncPromise::FULFILLED);
|
$this->assertValidPromise(
|
||||||
|
$promise,
|
||||||
|
null,
|
||||||
|
['1', 'value1', 'value2', 3, 'value2-value3', []],
|
||||||
|
SyncPromise::FULFILLED
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWait() : void
|
public function testWait() : void
|
||||||
{
|
{
|
||||||
$called = [];
|
$called = [];
|
||||||
|
|
||||||
$deferred1 = new Deferred(function() use (&$called) {
|
$deferred1 = new Deferred(function () use (&$called) {
|
||||||
$called[] = 1;
|
$called[] = 1;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
$deferred2 = new Deferred(function() use (&$called) {
|
$deferred2 = new Deferred(function () use (&$called) {
|
||||||
$called[] = 2;
|
$called[] = 2;
|
||||||
|
|
||||||
return 2;
|
return 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
$p1 = $this->promises->convertThenable($deferred1);
|
$p1 = $this->promises->convertThenable($deferred1);
|
||||||
$p2 = $this->promises->convertThenable($deferred2);
|
$p2 = $this->promises->convertThenable($deferred2);
|
||||||
|
|
||||||
$p3 = $p2->then(function() use (&$called) {
|
$p3 = $p2->then(function () use (&$called) {
|
||||||
$dfd = new Deferred(function() use (&$called) {
|
$dfd = new Deferred(function () use (&$called) {
|
||||||
$called[] = 3;
|
$called[] = 3;
|
||||||
|
|
||||||
return 3;
|
return 3;
|
||||||
});
|
});
|
||||||
|
|
||||||
return $this->promises->convertThenable($dfd);
|
return $this->promises->convertThenable($dfd);
|
||||||
});
|
});
|
||||||
|
|
||||||
$p4 = $p3->then(function() use (&$called) {
|
$p4 = $p3->then(function () use (&$called) {
|
||||||
return new Deferred(function() use (&$called) {
|
return new Deferred(function () use (&$called) {
|
||||||
$called[] = 4;
|
$called[] = 4;
|
||||||
|
|
||||||
return 4;
|
return 4;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -156,46 +210,10 @@ class SyncPromiseAdapterTest extends TestCase
|
|||||||
$this->assertEquals(SyncPromise::PENDING, $all->adoptedPromise->state);
|
$this->assertEquals(SyncPromise::PENDING, $all->adoptedPromise->state);
|
||||||
$this->assertEquals([1, 2], $called);
|
$this->assertEquals([1, 2], $called);
|
||||||
|
|
||||||
$expectedResult = [0,1,2,3,4];
|
$expectedResult = [0, 1, 2, 3, 4];
|
||||||
$result = $this->promises->wait($all);
|
$result = $this->promises->wait($all);
|
||||||
$this->assertEquals($expectedResult, $result);
|
$this->assertEquals($expectedResult, $result);
|
||||||
$this->assertEquals([1, 2, 3, 4], $called);
|
$this->assertEquals([1, 2, 3, 4], $called);
|
||||||
$this->assertValidPromise($all, null, [0,1,2,3,4], SyncPromise::FULFILLED);
|
$this->assertValidPromise($all, null, [0, 1, 2, 3, 4], SyncPromise::FULFILLED);
|
||||||
}
|
|
||||||
|
|
||||||
private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
|
|
||||||
{
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise);
|
|
||||||
$this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise);
|
|
||||||
|
|
||||||
$actualNextValue = null;
|
|
||||||
$actualNextReason = null;
|
|
||||||
$onFulfilledCalled = false;
|
|
||||||
$onRejectedCalled = false;
|
|
||||||
|
|
||||||
$promise->then(
|
|
||||||
function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
|
|
||||||
$onFulfilledCalled = true;
|
|
||||||
$actualNextValue = $nextValue;
|
|
||||||
},
|
|
||||||
function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) {
|
|
||||||
$onRejectedCalled = true;
|
|
||||||
$actualNextReason = $reason->getMessage();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertSame($onFulfilledCalled, false);
|
|
||||||
$this->assertSame($onRejectedCalled, false);
|
|
||||||
|
|
||||||
SyncPromise::runQueue();
|
|
||||||
|
|
||||||
if ($expectedNextState !== SyncPromise::PENDING) {
|
|
||||||
$this->assertSame(!$expectedNextReason, $onFulfilledCalled);
|
|
||||||
$this->assertSame(!!$expectedNextReason, $onRejectedCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertSame($expectedNextValue, $actualNextValue);
|
|
||||||
$this->assertSame($expectedNextReason, $actualNextReason);
|
|
||||||
$this->assertSame($expectedNextState, $promise->adoptedPromise->state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor\Promise;
|
namespace GraphQL\Tests\Executor\Promise;
|
||||||
|
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
use GraphQL\Executor\Promise\Adapter\SyncPromise;
|
||||||
use PHPUnit\Framework\Error\Error;
|
use PHPUnit\Framework\Error\Error;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function uniqid;
|
||||||
|
|
||||||
class SyncPromiseTest extends TestCase
|
class SyncPromiseTest extends TestCase
|
||||||
{
|
{
|
||||||
public function getFulfilledPromiseResolveData()
|
public function getFulfilledPromiseResolveData()
|
||||||
{
|
{
|
||||||
$onFulfilledReturnsNull = function() {
|
$onFulfilledReturnsNull = function () {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
$onFulfilledReturnsSameValue = function($value) {
|
|
||||||
|
$onFulfilledReturnsSameValue = function ($value) {
|
||||||
return $value;
|
return $value;
|
||||||
};
|
};
|
||||||
$onFulfilledReturnsOtherValue = function($value) {
|
|
||||||
|
$onFulfilledReturnsOtherValue = function ($value) {
|
||||||
return 'other-' . $value;
|
return 'other-' . $value;
|
||||||
};
|
};
|
||||||
$onFulfilledThrows = function($value) {
|
|
||||||
throw new \Exception("onFulfilled throws this!");
|
$onFulfilledThrows = function ($value) {
|
||||||
|
throw new \Exception('onFulfilled throws this!');
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -28,7 +35,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
[uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED],
|
[uniqid(), $onFulfilledReturnsNull, null, null, SyncPromise::FULFILLED],
|
||||||
['test-value', $onFulfilledReturnsSameValue, 'test-value', null, SyncPromise::FULFILLED],
|
['test-value', $onFulfilledReturnsSameValue, 'test-value', null, SyncPromise::FULFILLED],
|
||||||
['test-value-2', $onFulfilledReturnsOtherValue, 'other-test-value-2', null, SyncPromise::FULFILLED],
|
['test-value-2', $onFulfilledReturnsOtherValue, 'other-test-value-2', null, SyncPromise::FULFILLED],
|
||||||
['test-value-3', $onFulfilledThrows, null, "onFulfilled throws this!", SyncPromise::REJECTED],
|
['test-value-3', $onFulfilledThrows, null, 'onFulfilled throws this!', SyncPromise::REJECTED],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +48,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
@ -63,8 +69,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
@ -85,21 +90,27 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
$promise->resolve($resolvedValue);
|
$promise->resolve($resolvedValue);
|
||||||
$this->assertEquals(SyncPromise::FULFILLED, $promise->state);
|
$this->assertEquals(SyncPromise::FULFILLED, $promise->state);
|
||||||
|
|
||||||
$nextPromise = $promise->then(null, function() {});
|
$nextPromise = $promise->then(
|
||||||
|
null,
|
||||||
|
function () {
|
||||||
|
}
|
||||||
|
);
|
||||||
$this->assertSame($promise, $nextPromise);
|
$this->assertSame($promise, $nextPromise);
|
||||||
|
|
||||||
$onRejectedCalled = false;
|
$onRejectedCalled = false;
|
||||||
$nextPromise = $promise->then($onFulfilled, function () use (&$onRejectedCalled) {
|
$nextPromise = $promise->then(
|
||||||
$onRejectedCalled = true;
|
$onFulfilled,
|
||||||
});
|
function () use (&$onRejectedCalled) {
|
||||||
|
$onRejectedCalled = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if ($onFulfilled) {
|
if ($onFulfilled) {
|
||||||
$this->assertNotSame($promise, $nextPromise);
|
$this->assertNotSame($promise, $nextPromise);
|
||||||
@ -124,19 +135,57 @@ class SyncPromiseTest extends TestCase
|
|||||||
$this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState);
|
$this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertValidPromise(
|
||||||
|
SyncPromise $promise,
|
||||||
|
$expectedNextReason,
|
||||||
|
$expectedNextValue,
|
||||||
|
$expectedNextState
|
||||||
|
) {
|
||||||
|
$actualNextValue = null;
|
||||||
|
$actualNextReason = null;
|
||||||
|
$onFulfilledCalled = false;
|
||||||
|
$onRejectedCalled = false;
|
||||||
|
|
||||||
|
$promise->then(
|
||||||
|
function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
|
||||||
|
$onFulfilledCalled = true;
|
||||||
|
$actualNextValue = $nextValue;
|
||||||
|
},
|
||||||
|
function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) {
|
||||||
|
$onRejectedCalled = true;
|
||||||
|
$actualNextReason = $reason->getMessage();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($onFulfilledCalled, false);
|
||||||
|
$this->assertEquals($onRejectedCalled, false);
|
||||||
|
|
||||||
|
SyncPromise::runQueue();
|
||||||
|
|
||||||
|
$this->assertEquals(! $expectedNextReason, $onFulfilledCalled);
|
||||||
|
$this->assertEquals(! ! $expectedNextReason, $onRejectedCalled);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedNextValue, $actualNextValue);
|
||||||
|
$this->assertEquals($expectedNextReason, $actualNextReason);
|
||||||
|
$this->assertEquals($expectedNextState, $promise->state);
|
||||||
|
}
|
||||||
|
|
||||||
public function getRejectedPromiseData()
|
public function getRejectedPromiseData()
|
||||||
{
|
{
|
||||||
$onRejectedReturnsNull = function() {
|
$onRejectedReturnsNull = function () {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
$onRejectedReturnsSomeValue = function($reason) {
|
|
||||||
|
$onRejectedReturnsSomeValue = function ($reason) {
|
||||||
return 'some-value';
|
return 'some-value';
|
||||||
};
|
};
|
||||||
$onRejectedThrowsSameReason = function($reason) {
|
|
||||||
|
$onRejectedThrowsSameReason = function ($reason) {
|
||||||
throw $reason;
|
throw $reason;
|
||||||
};
|
};
|
||||||
$onRejectedThrowsOtherReason = function($value) {
|
|
||||||
throw new \Exception("onRejected throws other!");
|
$onRejectedThrowsOtherReason = function ($value) {
|
||||||
|
throw new \Exception('onRejected throws other!');
|
||||||
};
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -158,8 +207,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
@ -169,7 +217,6 @@ class SyncPromiseTest extends TestCase
|
|||||||
$this->expectException(\Throwable::class);
|
$this->expectException(\Throwable::class);
|
||||||
$this->expectExceptionMessage('Cannot change rejection reason');
|
$this->expectExceptionMessage('Cannot change rejection reason');
|
||||||
$promise->reject(new \Exception('other-reason'));
|
$promise->reject(new \Exception('other-reason'));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,8 +228,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
@ -203,8 +249,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
$expectedNextValue,
|
$expectedNextValue,
|
||||||
$expectedNextReason,
|
$expectedNextReason,
|
||||||
$expectedNextState
|
$expectedNextState
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
|
|
||||||
@ -214,22 +259,26 @@ class SyncPromiseTest extends TestCase
|
|||||||
try {
|
try {
|
||||||
$promise->reject(new \Exception('other-reason'));
|
$promise->reject(new \Exception('other-reason'));
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->assertEquals('Cannot change rejection reason', $e->getMessage());
|
$this->assertEquals('Cannot change rejection reason', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$promise->resolve('anything');
|
$promise->resolve('anything');
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->assertEquals('Cannot resolve rejected promise', $e->getMessage());
|
$this->assertEquals('Cannot resolve rejected promise', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$nextPromise = $promise->then(function() {}, null);
|
$nextPromise = $promise->then(
|
||||||
|
function () {
|
||||||
|
},
|
||||||
|
null
|
||||||
|
);
|
||||||
$this->assertSame($promise, $nextPromise);
|
$this->assertSame($promise, $nextPromise);
|
||||||
|
|
||||||
$onFulfilledCalled = false;
|
$onFulfilledCalled = false;
|
||||||
$nextPromise = $promise->then(
|
$nextPromise = $promise->then(
|
||||||
function () use (&$onFulfilledCalled) {
|
function () use (&$onFulfilledCalled) {
|
||||||
$onFulfilledCalled = true;
|
$onFulfilledCalled = true;
|
||||||
},
|
},
|
||||||
@ -266,7 +315,7 @@ class SyncPromiseTest extends TestCase
|
|||||||
try {
|
try {
|
||||||
$promise->resolve($promise);
|
$promise->resolve($promise);
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->assertEquals('Cannot resolve promise with self', $e->getMessage());
|
$this->assertEquals('Cannot resolve promise with self', $e->getMessage());
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
}
|
}
|
||||||
@ -299,24 +348,25 @@ class SyncPromiseTest extends TestCase
|
|||||||
throw $e;
|
throw $e;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->assertEquals(SyncPromise::PENDING, $promise->state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$promise->reject(new \Exception("Rejected Reason"));
|
$promise->reject(new \Exception('Rejected Reason'));
|
||||||
$this->assertValidPromise($promise, "Rejected Reason", null, SyncPromise::REJECTED);
|
$this->assertValidPromise($promise, 'Rejected Reason', null, SyncPromise::REJECTED);
|
||||||
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$promise2 = $promise->then(null, function() {
|
$promise2 = $promise->then(
|
||||||
return 'value';
|
null,
|
||||||
});
|
function () {
|
||||||
$promise->reject(new \Exception("Rejected Again"));
|
return 'value';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$promise->reject(new \Exception('Rejected Again'));
|
||||||
$this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED);
|
$this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED);
|
||||||
|
|
||||||
$promise = new SyncPromise();
|
$promise = new SyncPromise();
|
||||||
$promise2 = $promise->then();
|
$promise2 = $promise->then();
|
||||||
$promise->reject(new \Exception("Rejected Once Again"));
|
$promise->reject(new \Exception('Rejected Once Again'));
|
||||||
$this->assertValidPromise($promise2, "Rejected Once Again", null, SyncPromise::REJECTED);
|
$this->assertValidPromise($promise2, 'Rejected Once Again', null, SyncPromise::REJECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPendingPromiseThen() : void
|
public function testPendingPromiseThen() : void
|
||||||
@ -331,12 +381,14 @@ class SyncPromiseTest extends TestCase
|
|||||||
|
|
||||||
// Make sure that it queues derivative promises until resolution:
|
// Make sure that it queues derivative promises until resolution:
|
||||||
$onFulfilledCount = 0;
|
$onFulfilledCount = 0;
|
||||||
$onRejectedCount = 0;
|
$onRejectedCount = 0;
|
||||||
$onFulfilled = function($value) use (&$onFulfilledCount) {
|
$onFulfilled = function ($value) use (&$onFulfilledCount) {
|
||||||
$onFulfilledCount++;
|
$onFulfilledCount++;
|
||||||
|
|
||||||
return $onFulfilledCount;
|
return $onFulfilledCount;
|
||||||
};
|
};
|
||||||
$onRejected = function($reason) use (&$onRejectedCount) {
|
|
||||||
|
$onRejected = function ($reason) use (&$onRejectedCount) {
|
||||||
$onRejectedCount++;
|
$onRejectedCount++;
|
||||||
throw $reason;
|
throw $reason;
|
||||||
};
|
};
|
||||||
@ -367,35 +419,4 @@ class SyncPromiseTest extends TestCase
|
|||||||
$this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED);
|
$this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED);
|
||||||
$this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED);
|
$this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertValidPromise(SyncPromise $promise, $expectedNextReason, $expectedNextValue, $expectedNextState)
|
|
||||||
{
|
|
||||||
$actualNextValue = null;
|
|
||||||
$actualNextReason = null;
|
|
||||||
$onFulfilledCalled = false;
|
|
||||||
$onRejectedCalled = false;
|
|
||||||
|
|
||||||
$promise->then(
|
|
||||||
function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) {
|
|
||||||
$onFulfilledCalled = true;
|
|
||||||
$actualNextValue = $nextValue;
|
|
||||||
},
|
|
||||||
function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) {
|
|
||||||
$onRejectedCalled = true;
|
|
||||||
$actualNextReason = $reason->getMessage();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals($onFulfilledCalled, false);
|
|
||||||
$this->assertEquals($onRejectedCalled, false);
|
|
||||||
|
|
||||||
SyncPromise::runQueue();
|
|
||||||
|
|
||||||
$this->assertEquals(!$expectedNextReason, $onFulfilledCalled);
|
|
||||||
$this->assertEquals(!!$expectedNextReason, $onRejectedCalled);
|
|
||||||
|
|
||||||
$this->assertEquals($expectedNextValue, $actualNextValue);
|
|
||||||
$this->assertEquals($expectedNextReason, $actualNextReason);
|
|
||||||
$this->assertEquals($expectedNextState, $promise->state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Tests\Executor\TestClasses\Adder;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function json_encode;
|
||||||
|
use function uniqid;
|
||||||
|
|
||||||
class ResolveTest extends TestCase
|
class ResolveTest extends TestCase
|
||||||
{
|
{
|
||||||
// Execute: resolve function
|
// Execute: resolve function
|
||||||
|
|
||||||
private function buildSchema($testField)
|
|
||||||
{
|
|
||||||
return new Schema([
|
|
||||||
'query' => new ObjectType([
|
|
||||||
'name' => 'Query',
|
|
||||||
'fields' => [
|
|
||||||
'test' => $testField
|
|
||||||
]
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('default function accesses properties')
|
* @see it('default function accesses properties')
|
||||||
*/
|
*/
|
||||||
@ -32,9 +24,7 @@ class ResolveTest extends TestCase
|
|||||||
{
|
{
|
||||||
$schema = $this->buildSchema(['type' => Type::string()]);
|
$schema = $this->buildSchema(['type' => Type::string()]);
|
||||||
|
|
||||||
$source = [
|
$source = ['test' => 'testValue'];
|
||||||
'test' => 'testValue'
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
['data' => ['test' => 'testValue']],
|
['data' => ['test' => 'testValue']],
|
||||||
@ -42,18 +32,28 @@ class ResolveTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildSchema($testField)
|
||||||
|
{
|
||||||
|
return new Schema([
|
||||||
|
'query' => new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => ['test' => $testField],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('default function calls methods')
|
* @see it('default function calls methods')
|
||||||
*/
|
*/
|
||||||
public function testDefaultFunctionCallsClosures() : void
|
public function testDefaultFunctionCallsClosures() : void
|
||||||
{
|
{
|
||||||
$schema = $this->buildSchema(['type' => Type::string()]);
|
$schema = $this->buildSchema(['type' => Type::string()]);
|
||||||
$_secret = 'secretValue' . uniqid();
|
$_secret = 'secretValue' . uniqid();
|
||||||
|
|
||||||
$source = [
|
$source = [
|
||||||
'test' => function() use ($_secret) {
|
'test' => function () use ($_secret) {
|
||||||
return $_secret;
|
return $_secret;
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
['data' => ['test' => $_secret]],
|
['data' => ['test' => $_secret]],
|
||||||
@ -69,7 +69,7 @@ class ResolveTest extends TestCase
|
|||||||
$schema = $this->buildSchema([
|
$schema = $this->buildSchema([
|
||||||
'type' => Type::int(),
|
'type' => Type::int(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'addend1' => [ 'type' => Type::int() ],
|
'addend1' => ['type' => Type::int()],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -85,14 +85,14 @@ class ResolveTest extends TestCase
|
|||||||
public function testUsesProvidedResolveFunction() : void
|
public function testUsesProvidedResolveFunction() : void
|
||||||
{
|
{
|
||||||
$schema = $this->buildSchema([
|
$schema = $this->buildSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'aStr' => ['type' => Type::string()],
|
'aStr' => ['type' => Type::string()],
|
||||||
'aInt' => ['type' => Type::int()],
|
'aInt' => ['type' => Type::int()],
|
||||||
],
|
],
|
||||||
'resolve' => function ($source, $args) {
|
'resolve' => function ($source, $args) {
|
||||||
return json_encode([$source, $args]);
|
return json_encode([$source, $args]);
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Error\Error;
|
|
||||||
use GraphQL\Error\FormattedError;
|
use GraphQL\Error\FormattedError;
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
@ -29,37 +31,38 @@ class SyncTest extends TestCase
|
|||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->schema = new Schema([
|
$this->schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'syncField' => [
|
'syncField' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function ($rootValue) {
|
'resolve' => function ($rootValue) {
|
||||||
return $rootValue;
|
return $rootValue;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'asyncField' => [
|
'asyncField' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function ($rootValue) {
|
'resolve' => function ($rootValue) {
|
||||||
return new Deferred(function () use ($rootValue) {
|
return new Deferred(function () use ($rootValue) {
|
||||||
return $rootValue;
|
return $rootValue;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'mutation' => new ObjectType([
|
'mutation' => new ObjectType([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'syncMutationField' => [
|
'syncMutationField' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function ($rootValue) {
|
'resolve' => function ($rootValue) {
|
||||||
return $rootValue;
|
return $rootValue;
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->promiseAdapter = new SyncPromiseAdapter();
|
$this->promiseAdapter = new SyncPromiseAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ class SyncTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotReturnAPromiseForInitialErrors() : void
|
public function testDoesNotReturnAPromiseForInitialErrors() : void
|
||||||
{
|
{
|
||||||
$doc = 'fragment Example on Query { syncField }';
|
$doc = 'fragment Example on Query { syncField }';
|
||||||
$result = $this->execute(
|
$result = $this->execute(
|
||||||
$this->schema,
|
$this->schema,
|
||||||
Parser::parse($doc),
|
Parser::parse($doc),
|
||||||
@ -79,106 +82,6 @@ class SyncTest extends TestCase
|
|||||||
$this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result);
|
$this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('does not return a Promise if fields are all synchronous')
|
|
||||||
*/
|
|
||||||
public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void
|
|
||||||
{
|
|
||||||
$doc = 'query Example { syncField }';
|
|
||||||
$result = $this->execute(
|
|
||||||
$this->schema,
|
|
||||||
Parser::parse($doc),
|
|
||||||
'rootValue'
|
|
||||||
);
|
|
||||||
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('does not return a Promise if mutation fields are all synchronous')
|
|
||||||
*/
|
|
||||||
public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void
|
|
||||||
{
|
|
||||||
$doc = 'mutation Example { syncMutationField }';
|
|
||||||
$result = $this->execute(
|
|
||||||
$this->schema,
|
|
||||||
Parser::parse($doc),
|
|
||||||
'rootValue'
|
|
||||||
);
|
|
||||||
$this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('returns a Promise if any field is asynchronous')
|
|
||||||
*/
|
|
||||||
public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void
|
|
||||||
{
|
|
||||||
$doc = 'query Example { syncField, asyncField }';
|
|
||||||
$result = $this->execute(
|
|
||||||
$this->schema,
|
|
||||||
Parser::parse($doc),
|
|
||||||
'rootValue'
|
|
||||||
);
|
|
||||||
$this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe: graphqlSync
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('does not return a Promise for syntax errors')
|
|
||||||
*/
|
|
||||||
public function testDoesNotReturnAPromiseForSyntaxErrors() : void
|
|
||||||
{
|
|
||||||
$doc = 'fragment Example on Query { { { syncField }';
|
|
||||||
$result = $this->graphqlSync(
|
|
||||||
$this->schema,
|
|
||||||
$doc
|
|
||||||
);
|
|
||||||
$this->assertSync([
|
|
||||||
'errors' => [
|
|
||||||
['message' => 'Syntax Error: Expected Name, found {',
|
|
||||||
'locations' => [['line' => 1, 'column' => 29]]]
|
|
||||||
]
|
|
||||||
], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('does not return a Promise for validation errors')
|
|
||||||
*/
|
|
||||||
public function testDoesNotReturnAPromiseForValidationErrors() : void
|
|
||||||
{
|
|
||||||
$doc = 'fragment Example on Query { unknownField }';
|
|
||||||
$validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc));
|
|
||||||
$result = $this->graphqlSync(
|
|
||||||
$this->schema,
|
|
||||||
$doc
|
|
||||||
);
|
|
||||||
$expected = [
|
|
||||||
'errors' => Utils::map($validationErrors, function ($e) {
|
|
||||||
return FormattedError::createFromException($e);
|
|
||||||
})
|
|
||||||
];
|
|
||||||
$this->assertSync($expected, $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see it('does not return a Promise for sync execution')
|
|
||||||
*/
|
|
||||||
public function testDoesNotReturnAPromiseForSyncExecution() : void
|
|
||||||
{
|
|
||||||
$doc = 'query Example { syncField }';
|
|
||||||
$result = $this->graphqlSync(
|
|
||||||
$this->schema,
|
|
||||||
$doc,
|
|
||||||
'rootValue'
|
|
||||||
);
|
|
||||||
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function graphqlSync($schema, $doc, $rootValue = null)
|
|
||||||
{
|
|
||||||
return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function execute($schema, $doc, $rootValue = null)
|
private function execute($schema, $doc, $rootValue = null)
|
||||||
{
|
{
|
||||||
return Executor::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
|
return Executor::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
|
||||||
@ -191,7 +94,56 @@ class SyncTest extends TestCase
|
|||||||
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
|
$this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message);
|
||||||
$this->assertEquals(SyncPromise::FULFILLED, $actualResult->adoptedPromise->state, $message);
|
$this->assertEquals(SyncPromise::FULFILLED, $actualResult->adoptedPromise->state, $message);
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $actualResult->adoptedPromise->result, $message);
|
$this->assertInstanceOf(ExecutionResult::class, $actualResult->adoptedPromise->result, $message);
|
||||||
$this->assertArraySubset($expectedFinalArray, $actualResult->adoptedPromise->result->toArray(), $message);
|
$this->assertArraySubset(
|
||||||
|
$expectedFinalArray,
|
||||||
|
$actualResult->adoptedPromise->result->toArray(),
|
||||||
|
false,
|
||||||
|
$message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('does not return a Promise if fields are all synchronous')
|
||||||
|
*/
|
||||||
|
public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void
|
||||||
|
{
|
||||||
|
$doc = 'query Example { syncField }';
|
||||||
|
$result = $this->execute(
|
||||||
|
$this->schema,
|
||||||
|
Parser::parse($doc),
|
||||||
|
'rootValue'
|
||||||
|
);
|
||||||
|
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe: graphqlSync
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('does not return a Promise if mutation fields are all synchronous')
|
||||||
|
*/
|
||||||
|
public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void
|
||||||
|
{
|
||||||
|
$doc = 'mutation Example { syncMutationField }';
|
||||||
|
$result = $this->execute(
|
||||||
|
$this->schema,
|
||||||
|
Parser::parse($doc),
|
||||||
|
'rootValue'
|
||||||
|
);
|
||||||
|
$this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('returns a Promise if any field is asynchronous')
|
||||||
|
*/
|
||||||
|
public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void
|
||||||
|
{
|
||||||
|
$doc = 'query Example { syncField, asyncField }';
|
||||||
|
$result = $this->execute(
|
||||||
|
$this->schema,
|
||||||
|
Parser::parse($doc),
|
||||||
|
'rootValue'
|
||||||
|
);
|
||||||
|
$this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertAsync($expectedFinalArray, $actualResult)
|
private function assertAsync($expectedFinalArray, $actualResult)
|
||||||
@ -202,6 +154,70 @@ class SyncTest extends TestCase
|
|||||||
$this->assertEquals(SyncPromise::PENDING, $actualResult->adoptedPromise->state, $message);
|
$this->assertEquals(SyncPromise::PENDING, $actualResult->adoptedPromise->state, $message);
|
||||||
$resolvedResult = $this->promiseAdapter->wait($actualResult);
|
$resolvedResult = $this->promiseAdapter->wait($actualResult);
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $resolvedResult, $message);
|
$this->assertInstanceOf(ExecutionResult::class, $resolvedResult, $message);
|
||||||
$this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), $message);
|
$this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), false, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('does not return a Promise for syntax errors')
|
||||||
|
*/
|
||||||
|
public function testDoesNotReturnAPromiseForSyntaxErrors() : void
|
||||||
|
{
|
||||||
|
$doc = 'fragment Example on Query { { { syncField }';
|
||||||
|
$result = $this->graphqlSync(
|
||||||
|
$this->schema,
|
||||||
|
$doc
|
||||||
|
);
|
||||||
|
$this->assertSync(
|
||||||
|
[
|
||||||
|
'errors' => [
|
||||||
|
[
|
||||||
|
'message' => 'Syntax Error: Expected Name, found {',
|
||||||
|
'locations' => [['line' => 1, 'column' => 29]],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function graphqlSync($schema, $doc, $rootValue = null)
|
||||||
|
{
|
||||||
|
return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('does not return a Promise for validation errors')
|
||||||
|
*/
|
||||||
|
public function testDoesNotReturnAPromiseForValidationErrors() : void
|
||||||
|
{
|
||||||
|
$doc = 'fragment Example on Query { unknownField }';
|
||||||
|
$validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc));
|
||||||
|
$result = $this->graphqlSync(
|
||||||
|
$this->schema,
|
||||||
|
$doc
|
||||||
|
);
|
||||||
|
$expected = [
|
||||||
|
'errors' => Utils::map(
|
||||||
|
$validationErrors,
|
||||||
|
function ($e) {
|
||||||
|
return FormattedError::createFromException($e);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
];
|
||||||
|
$this->assertSync($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('does not return a Promise for sync execution')
|
||||||
|
*/
|
||||||
|
public function testDoesNotReturnAPromiseForSyncExecution() : void
|
||||||
|
{
|
||||||
|
$doc = 'query Example { syncField }';
|
||||||
|
$result = $this->graphqlSync(
|
||||||
|
$this->schema,
|
||||||
|
$doc,
|
||||||
|
'rootValue'
|
||||||
|
);
|
||||||
|
$this->assertSync(['data' => ['syncField' => 'rootValue']], $result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace GraphQL\Tests\Executor;
|
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
|
||||||
use GraphQL\Type\Definition\ScalarType;
|
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
|
|
||||||
class Dog
|
|
||||||
{
|
|
||||||
function __construct($name, $woofs)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->woofs = $woofs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Cat
|
|
||||||
{
|
|
||||||
function __construct($name, $meows)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->meows = $meows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Human
|
|
||||||
{
|
|
||||||
function __construct($name)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Person
|
|
||||||
{
|
|
||||||
public $name;
|
|
||||||
public $pets;
|
|
||||||
public $friends;
|
|
||||||
|
|
||||||
function __construct($name, $pets = null, $friends = null)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->pets = $pets;
|
|
||||||
$this->friends = $friends;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComplexScalar extends ScalarType
|
|
||||||
{
|
|
||||||
public static function create()
|
|
||||||
{
|
|
||||||
return new self();
|
|
||||||
}
|
|
||||||
|
|
||||||
public $name = 'ComplexScalar';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function serialize($value)
|
|
||||||
{
|
|
||||||
if ($value === 'DeserializedValue') {
|
|
||||||
return 'SerializedValue';
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Cannot serialize value as ComplexScalar: " . Utils::printSafe($value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function parseValue($value)
|
|
||||||
{
|
|
||||||
if ($value === 'SerializedValue') {
|
|
||||||
return 'DeserializedValue';
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Cannot represent value as ComplexScalar: " . Utils::printSafe($value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function parseLiteral($valueNode, array $variables = null)
|
|
||||||
{
|
|
||||||
if ($valueNode->value === 'SerializedValue') {
|
|
||||||
return 'DeserializedValue';
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Cannot represent literal as ComplexScalar: " . Utils::printSafe($valueNode->value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Special
|
|
||||||
{
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
public function __construct($value)
|
|
||||||
{
|
|
||||||
$this->value = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotSpecial
|
|
||||||
{
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
public function __construct($value)
|
|
||||||
{
|
|
||||||
$this->value = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Adder
|
|
||||||
{
|
|
||||||
public $num;
|
|
||||||
|
|
||||||
public $test;
|
|
||||||
|
|
||||||
public function __construct($num)
|
|
||||||
{
|
|
||||||
$this->num = $num;
|
|
||||||
|
|
||||||
$this->test = function($source, $args, $context) {
|
|
||||||
return $this->num + $args['addend1'] + $context['addend2'];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
23
tests/Executor/TestClasses/Adder.php
Normal file
23
tests/Executor/TestClasses/Adder.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Adder
|
||||||
|
{
|
||||||
|
/** @var float */
|
||||||
|
public $num;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
public $test;
|
||||||
|
|
||||||
|
public function __construct(float $num)
|
||||||
|
{
|
||||||
|
$this->num = $num;
|
||||||
|
|
||||||
|
$this->test = function ($source, $args, $context) {
|
||||||
|
return $this->num + $args['addend1'] + $context['addend2'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
20
tests/Executor/TestClasses/Cat.php
Normal file
20
tests/Executor/TestClasses/Cat.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Cat
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
public $meows;
|
||||||
|
|
||||||
|
public function __construct(string $name, bool $meows)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->meows = $meows;
|
||||||
|
}
|
||||||
|
}
|
56
tests/Executor/TestClasses/ComplexScalar.php
Normal file
56
tests/Executor/TestClasses/ComplexScalar.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
use GraphQL\Error\Error;
|
||||||
|
use GraphQL\Type\Definition\ScalarType;
|
||||||
|
use GraphQL\Utils\Utils;
|
||||||
|
|
||||||
|
class ComplexScalar extends ScalarType
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $name = 'ComplexScalar';
|
||||||
|
|
||||||
|
public static function create() : self
|
||||||
|
{
|
||||||
|
return new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function serialize($value)
|
||||||
|
{
|
||||||
|
if ($value === 'DeserializedValue') {
|
||||||
|
return 'SerializedValue';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Cannot serialize value as ComplexScalar: ' . Utils::printSafe($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function parseValue($value)
|
||||||
|
{
|
||||||
|
if ($value === 'SerializedValue') {
|
||||||
|
return 'DeserializedValue';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Cannot represent value as ComplexScalar: ' . Utils::printSafe($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function parseLiteral($valueNode, ?array $variables = null)
|
||||||
|
{
|
||||||
|
if ($valueNode->value === 'SerializedValue') {
|
||||||
|
return 'DeserializedValue';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Cannot represent literal as ComplexScalar: ' . Utils::printSafe($valueNode->value));
|
||||||
|
}
|
||||||
|
}
|
20
tests/Executor/TestClasses/Dog.php
Normal file
20
tests/Executor/TestClasses/Dog.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Dog
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
public $woofs;
|
||||||
|
|
||||||
|
public function __construct(string $name, bool $woofs)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->woofs = $woofs;
|
||||||
|
}
|
||||||
|
}
|
16
tests/Executor/TestClasses/Human.php
Normal file
16
tests/Executor/TestClasses/Human.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Human
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
public function __construct(string $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
}
|
19
tests/Executor/TestClasses/NotSpecial.php
Normal file
19
tests/Executor/TestClasses/NotSpecial.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class NotSpecial
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
}
|
16
tests/Executor/TestClasses/NumberHolder.php
Normal file
16
tests/Executor/TestClasses/NumberHolder.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class NumberHolder
|
||||||
|
{
|
||||||
|
/** @var float */
|
||||||
|
public $theNumber;
|
||||||
|
|
||||||
|
public function __construct(float $originalNumber)
|
||||||
|
{
|
||||||
|
$this->theNumber = $originalNumber;
|
||||||
|
}
|
||||||
|
}
|
28
tests/Executor/TestClasses/Person.php
Normal file
28
tests/Executor/TestClasses/Person.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Person
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/** @var (Dog|Cat)[]|null */
|
||||||
|
public $pets;
|
||||||
|
|
||||||
|
/** @var (Dog|Cat|Person)[]|null */
|
||||||
|
public $friends;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param (Cat|Dog)[]|null $pets
|
||||||
|
* @param (Cat|Dog|Person)[]|null $friends
|
||||||
|
*/
|
||||||
|
public function __construct(string $name, $pets = null, $friends = null)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->pets = $pets;
|
||||||
|
$this->friends = $friends;
|
||||||
|
}
|
||||||
|
}
|
44
tests/Executor/TestClasses/Root.php
Normal file
44
tests/Executor/TestClasses/Root.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
use GraphQL\Deferred;
|
||||||
|
|
||||||
|
class Root
|
||||||
|
{
|
||||||
|
/** @var NumberHolder */
|
||||||
|
public $numberHolder;
|
||||||
|
|
||||||
|
public function __construct(float $originalNumber)
|
||||||
|
{
|
||||||
|
$this->numberHolder = new NumberHolder($originalNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function promiseToChangeTheNumber($newNumber) : Deferred
|
||||||
|
{
|
||||||
|
return new Deferred(function () use ($newNumber) {
|
||||||
|
return $this->immediatelyChangeTheNumber($newNumber);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function immediatelyChangeTheNumber($newNumber) : NumberHolder
|
||||||
|
{
|
||||||
|
$this->numberHolder->theNumber = $newNumber;
|
||||||
|
|
||||||
|
return $this->numberHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failToChangeTheNumber() : void
|
||||||
|
{
|
||||||
|
throw new \Exception('Cannot change the number');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function promiseAndFailToChangeTheNumber() : Deferred
|
||||||
|
{
|
||||||
|
return new Deferred(function () {
|
||||||
|
$this->failToChangeTheNumber();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
19
tests/Executor/TestClasses/Special.php
Normal file
19
tests/Executor/TestClasses/Special.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor\TestClasses;
|
||||||
|
|
||||||
|
class Special
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
public $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value
|
||||||
|
*/
|
||||||
|
public function __construct($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,64 +1,76 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
|
||||||
|
|
||||||
use GraphQL\Error\Warning;
|
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Tests\Executor\TestClasses\Cat;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Dog;
|
||||||
|
use GraphQL\Tests\Executor\TestClasses\Person;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
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\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class UnionInterfaceTest extends TestCase
|
class UnionInterfaceTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var */
|
||||||
public $schema;
|
public $schema;
|
||||||
|
|
||||||
|
/** @var Cat */
|
||||||
public $garfield;
|
public $garfield;
|
||||||
|
|
||||||
|
/** @var Dog */
|
||||||
public $odie;
|
public $odie;
|
||||||
|
|
||||||
|
/** @var Person */
|
||||||
public $liz;
|
public $liz;
|
||||||
|
|
||||||
|
/** @var Person */
|
||||||
public $john;
|
public $john;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$NamedType = new InterfaceType([
|
$NamedType = new InterfaceType([
|
||||||
'name' => 'Named',
|
'name' => 'Named',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$DogType = new ObjectType([
|
$DogType = new ObjectType([
|
||||||
'name' => 'Dog',
|
'name' => 'Dog',
|
||||||
'interfaces' => [$NamedType],
|
'interfaces' => [$NamedType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'woofs' => ['type' => Type::boolean()]
|
'woofs' => ['type' => Type::boolean()],
|
||||||
],
|
],
|
||||||
'isTypeOf' => function ($value) {
|
'isTypeOf' => function ($value) {
|
||||||
return $value instanceof Dog;
|
return $value instanceof Dog;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$CatType = new ObjectType([
|
$CatType = new ObjectType([
|
||||||
'name' => 'Cat',
|
'name' => 'Cat',
|
||||||
'interfaces' => [$NamedType],
|
'interfaces' => [$NamedType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'meows' => ['type' => Type::boolean()]
|
'meows' => ['type' => Type::boolean()],
|
||||||
],
|
],
|
||||||
'isTypeOf' => function ($value) {
|
'isTypeOf' => function ($value) {
|
||||||
return $value instanceof Cat;
|
return $value instanceof Cat;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PetType = new UnionType([
|
$PetType = new UnionType([
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'types' => [$DogType, $CatType],
|
'types' => [$DogType, $CatType],
|
||||||
'resolveType' => function ($value) use ($DogType, $CatType) {
|
'resolveType' => function ($value) use ($DogType, $CatType) {
|
||||||
if ($value instanceof Dog) {
|
if ($value instanceof Dog) {
|
||||||
return $DogType;
|
return $DogType;
|
||||||
@ -66,32 +78,31 @@ class UnionInterfaceTest extends TestCase
|
|||||||
if ($value instanceof Cat) {
|
if ($value instanceof Cat) {
|
||||||
return $CatType;
|
return $CatType;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PersonType = new ObjectType([
|
$PersonType = new ObjectType([
|
||||||
'name' => 'Person',
|
'name' => 'Person',
|
||||||
'interfaces' => [$NamedType],
|
'interfaces' => [$NamedType],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'pets' => ['type' => Type::listOf($PetType)],
|
'pets' => ['type' => Type::listOf($PetType)],
|
||||||
'friends' => ['type' => Type::listOf($NamedType)]
|
'friends' => ['type' => Type::listOf($NamedType)],
|
||||||
],
|
],
|
||||||
'isTypeOf' => function ($value) {
|
'isTypeOf' => function ($value) {
|
||||||
return $value instanceof Person;
|
return $value instanceof Person;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->schema = new Schema([
|
$this->schema = new Schema([
|
||||||
'query' => $PersonType,
|
'query' => $PersonType,
|
||||||
'types' => [ $PetType ]
|
'types' => [$PetType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->garfield = new Cat('Garfield', false);
|
$this->garfield = new Cat('Garfield', false);
|
||||||
$this->odie = new Dog('Odie', true);
|
$this->odie = new Dog('Odie', true);
|
||||||
$this->liz = new Person('Liz');
|
$this->liz = new Person('Liz');
|
||||||
$this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]);
|
$this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute: Union and intersection types
|
// Execute: Union and intersection types
|
||||||
@ -101,7 +112,6 @@ class UnionInterfaceTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testCanIntrospectOnUnionAndIntersectionTypes() : void
|
public function testCanIntrospectOnUnionAndIntersectionTypes() : void
|
||||||
{
|
{
|
||||||
|
|
||||||
$ast = Parser::parse('
|
$ast = Parser::parse('
|
||||||
{
|
{
|
||||||
Named: __type(name: "Named") {
|
Named: __type(name: "Named") {
|
||||||
@ -128,33 +138,33 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'Named' => [
|
'Named' => [
|
||||||
'kind' => 'INTERFACE',
|
'kind' => 'INTERFACE',
|
||||||
'name' => 'Named',
|
'name' => 'Named',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
['name' => 'name']
|
['name' => 'name'],
|
||||||
],
|
],
|
||||||
'interfaces' => null,
|
'interfaces' => null,
|
||||||
'possibleTypes' => [
|
'possibleTypes' => [
|
||||||
['name' => 'Person'],
|
['name' => 'Person'],
|
||||||
['name' => 'Dog'],
|
['name' => 'Dog'],
|
||||||
['name' => 'Cat']
|
['name' => 'Cat'],
|
||||||
],
|
],
|
||||||
'enumValues' => null,
|
'enumValues' => null,
|
||||||
'inputFields' => null
|
'inputFields' => null,
|
||||||
],
|
],
|
||||||
'Pet' => [
|
'Pet' => [
|
||||||
'kind' => 'UNION',
|
'kind' => 'UNION',
|
||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'fields' => null,
|
'fields' => null,
|
||||||
'interfaces' => null,
|
'interfaces' => null,
|
||||||
'possibleTypes' => [
|
'possibleTypes' => [
|
||||||
['name' => 'Dog'],
|
['name' => 'Dog'],
|
||||||
['name' => 'Cat']
|
['name' => 'Cat'],
|
||||||
],
|
],
|
||||||
'enumValues' => null,
|
'enumValues' => null,
|
||||||
'inputFields' => null
|
'inputFields' => null,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray());
|
||||||
}
|
}
|
||||||
@ -165,7 +175,7 @@ class UnionInterfaceTest extends TestCase
|
|||||||
public function testExecutesUsingUnionTypes() : void
|
public function testExecutesUsingUnionTypes() : void
|
||||||
{
|
{
|
||||||
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
||||||
$ast = Parser::parse('
|
$ast = Parser::parse('
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
@ -180,12 +190,12 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'__typename' => 'Person',
|
'__typename' => 'Person',
|
||||||
'name' => 'John',
|
'name' => 'John',
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
@ -197,7 +207,7 @@ class UnionInterfaceTest extends TestCase
|
|||||||
public function testExecutesUnionTypesWithInlineFragments() : void
|
public function testExecutesUnionTypesWithInlineFragments() : void
|
||||||
{
|
{
|
||||||
// This is the valid version of the query in the above test.
|
// This is the valid version of the query in the above test.
|
||||||
$ast = Parser::parse('
|
$ast = Parser::parse('
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
@ -217,13 +227,13 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'__typename' => 'Person',
|
'__typename' => 'Person',
|
||||||
'name' => 'John',
|
'name' => 'John',
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
]
|
],
|
||||||
|
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
}
|
}
|
||||||
@ -234,7 +244,7 @@ class UnionInterfaceTest extends TestCase
|
|||||||
public function testExecutesUsingInterfaceTypes() : void
|
public function testExecutesUsingInterfaceTypes() : void
|
||||||
{
|
{
|
||||||
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
// NOTE: This is an *invalid* query, but it should be an *executable* query.
|
||||||
$ast = Parser::parse('
|
$ast = Parser::parse('
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
@ -249,12 +259,12 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'__typename' => 'Person',
|
'__typename' => 'Person',
|
||||||
'name' => 'John',
|
'name' => 'John',
|
||||||
'friends' => [
|
'friends' => [
|
||||||
['__typename' => 'Person', 'name' => 'Liz'],
|
['__typename' => 'Person', 'name' => 'Liz'],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
@ -266,7 +276,7 @@ class UnionInterfaceTest extends TestCase
|
|||||||
public function testExecutesInterfaceTypesWithInlineFragments() : void
|
public function testExecutesInterfaceTypesWithInlineFragments() : void
|
||||||
{
|
{
|
||||||
// This is the valid version of the query in the above test.
|
// This is the valid version of the query in the above test.
|
||||||
$ast = Parser::parse('
|
$ast = Parser::parse('
|
||||||
{
|
{
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
@ -285,12 +295,12 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'__typename' => 'Person',
|
'__typename' => 'Person',
|
||||||
'name' => 'John',
|
'name' => 'John',
|
||||||
'friends' => [
|
'friends' => [
|
||||||
['__typename' => 'Person', 'name' => 'Liz'],
|
['__typename' => 'Person', 'name' => 'Liz'],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true));
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true));
|
||||||
@ -336,16 +346,16 @@ class UnionInterfaceTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'__typename' => 'Person',
|
'__typename' => 'Person',
|
||||||
'name' => 'John',
|
'name' => 'John',
|
||||||
'pets' => [
|
'pets' => [
|
||||||
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
],
|
],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
['__typename' => 'Person', 'name' => 'Liz'],
|
['__typename' => 'Person', 'name' => 'Liz'],
|
||||||
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true]
|
['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
@ -356,36 +366,45 @@ class UnionInterfaceTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testGetsExecutionInfoInResolver() : void
|
public function testGetsExecutionInfoInResolver() : void
|
||||||
{
|
{
|
||||||
$encounteredContext = null;
|
$encounteredContext = null;
|
||||||
$encounteredSchema = null;
|
$encounteredSchema = null;
|
||||||
$encounteredRootValue = null;
|
$encounteredRootValue = null;
|
||||||
$PersonType2 = null;
|
$PersonType2 = null;
|
||||||
|
|
||||||
$NamedType2 = new InterfaceType([
|
$NamedType2 = new InterfaceType([
|
||||||
'name' => 'Named',
|
'name' => 'Named',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()]
|
'name' => ['type' => Type::string()],
|
||||||
],
|
],
|
||||||
'resolveType' => function ($obj, $context, ResolveInfo $info) use (&$encounteredContext, &$encounteredSchema, &$encounteredRootValue, &$PersonType2) {
|
'resolveType' => function (
|
||||||
$encounteredContext = $context;
|
$obj,
|
||||||
$encounteredSchema = $info->schema;
|
$context,
|
||||||
|
ResolveInfo $info
|
||||||
|
) use (
|
||||||
|
&$encounteredContext,
|
||||||
|
&
|
||||||
|
$encounteredSchema,
|
||||||
|
&$encounteredRootValue,
|
||||||
|
&$PersonType2
|
||||||
|
) {
|
||||||
|
$encounteredContext = $context;
|
||||||
|
$encounteredSchema = $info->schema;
|
||||||
$encounteredRootValue = $info->rootValue;
|
$encounteredRootValue = $info->rootValue;
|
||||||
|
|
||||||
return $PersonType2;
|
return $PersonType2;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$PersonType2 = new ObjectType([
|
$PersonType2 = new ObjectType([
|
||||||
'name' => 'Person',
|
'name' => 'Person',
|
||||||
'interfaces' => [$NamedType2],
|
'interfaces' => [$NamedType2],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'name' => ['type' => Type::string()],
|
'name' => ['type' => Type::string()],
|
||||||
'friends' => ['type' => Type::listOf($NamedType2)],
|
'friends' => ['type' => Type::listOf($NamedType2)],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema2 = new Schema([
|
$schema2 = new Schema(['query' => $PersonType2]);
|
||||||
'query' => $PersonType2
|
|
||||||
]);
|
|
||||||
|
|
||||||
$john2 = new Person('John', [], [$this->liz]);
|
$john2 = new Person('John', [], [$this->liz]);
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Executor\Values;
|
use GraphQL\Executor\Values;
|
||||||
@ -10,19 +13,97 @@ 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 count;
|
||||||
|
use function var_export;
|
||||||
|
use const PHP_EOL;
|
||||||
|
|
||||||
class ValuesTest extends TestCase
|
class ValuesTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var Schema */
|
||||||
|
private static $schema;
|
||||||
|
|
||||||
public function testGetIDVariableValues() : void
|
public function testGetIDVariableValues() : void
|
||||||
{
|
{
|
||||||
$this->expectInputVariablesMatchOutputVariables(['idInput' => '123456789']);
|
$this->expectInputVariablesMatchOutputVariables(['idInput' => '123456789']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
['errors'=> [], 'coerced' => ['idInput' => '123456789']],
|
['errors' => [], 'coerced' => ['idInput' => '123456789']],
|
||||||
self::runTestCase(['idInput' => 123456789]),
|
$this->runTestCase(['idInput' => 123456789]),
|
||||||
'Integer ID was not converted to string'
|
'Integer ID was not converted to string'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function expectInputVariablesMatchOutputVariables($variables) : void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$variables,
|
||||||
|
$this->runTestCase($variables)['coerced'],
|
||||||
|
'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $variables
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
private function runTestCase($variables) : array
|
||||||
|
{
|
||||||
|
return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getSchema() : Schema
|
||||||
|
{
|
||||||
|
if (! self::$schema) {
|
||||||
|
self::$schema = new Schema([
|
||||||
|
'query' => new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => [
|
||||||
|
'test' => [
|
||||||
|
'type' => Type::boolean(),
|
||||||
|
'args' => [
|
||||||
|
'idInput' => Type::id(),
|
||||||
|
'boolInput' => Type::boolean(),
|
||||||
|
'intInput' => Type::int(),
|
||||||
|
'stringInput' => Type::string(),
|
||||||
|
'floatInput' => Type::float(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return VariableDefinitionNode[]
|
||||||
|
*/
|
||||||
|
private static function getVariableDefinitionNodes() : array
|
||||||
|
{
|
||||||
|
$idInputDefinition = new VariableDefinitionNode([
|
||||||
|
'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]),
|
||||||
|
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])]),
|
||||||
|
]);
|
||||||
|
$boolInputDefinition = new VariableDefinitionNode([
|
||||||
|
'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]),
|
||||||
|
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])]),
|
||||||
|
]);
|
||||||
|
$intInputDefinition = new VariableDefinitionNode([
|
||||||
|
'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]),
|
||||||
|
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])]),
|
||||||
|
]);
|
||||||
|
$stringInputDefintion = new VariableDefinitionNode([
|
||||||
|
'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]),
|
||||||
|
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])]),
|
||||||
|
]);
|
||||||
|
$floatInputDefinition = new VariableDefinitionNode([
|
||||||
|
'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]),
|
||||||
|
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition];
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetBooleanVariableValues() : void
|
public function testGetBooleanVariableValues() : void
|
||||||
{
|
{
|
||||||
$this->expectInputVariablesMatchOutputVariables(['boolInput' => true]);
|
$this->expectInputVariablesMatchOutputVariables(['boolInput' => true]);
|
||||||
@ -64,11 +145,20 @@ class ValuesTest extends TestCase
|
|||||||
$this->expectGraphQLError(['idInput' => true]);
|
$this->expectGraphQLError(['idInput' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function expectGraphQLError($variables) : void
|
||||||
|
{
|
||||||
|
$result = $this->runTestCase($variables);
|
||||||
|
$this->assertGreaterThan(0, count($result['errors']));
|
||||||
|
}
|
||||||
|
|
||||||
public function testFloatForIDVariableThrowsError() : void
|
public function testFloatForIDVariableThrowsError() : void
|
||||||
{
|
{
|
||||||
$this->expectGraphQLError(['idInput' => 1.0]);
|
$this->expectGraphQLError(['idInput' => 1.0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers for running test cases and making assertions
|
||||||
|
*/
|
||||||
public function testStringForBooleanVariableThrowsError() : void
|
public function testStringForBooleanVariableThrowsError() : void
|
||||||
{
|
{
|
||||||
$this->expectGraphQLError(['boolInput' => 'true']);
|
$this->expectGraphQLError(['boolInput' => 'true']);
|
||||||
@ -98,77 +188,4 @@ class ValuesTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->expectGraphQLError(['intInput' => -2147483649]);
|
$this->expectGraphQLError(['intInput' => -2147483649]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers for running test cases and making assertions
|
|
||||||
|
|
||||||
private function expectInputVariablesMatchOutputVariables($variables)
|
|
||||||
{
|
|
||||||
$this->assertEquals(
|
|
||||||
$variables,
|
|
||||||
self::runTestCase($variables)['coerced'],
|
|
||||||
'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function expectGraphQLError($variables)
|
|
||||||
{
|
|
||||||
$result = self::runTestCase($variables);
|
|
||||||
$this->assertGreaterThan(0, count($result['errors']));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static $schema;
|
|
||||||
|
|
||||||
private static function getSchema()
|
|
||||||
{
|
|
||||||
if (!self::$schema) {
|
|
||||||
self::$schema = new Schema([
|
|
||||||
'query' => new ObjectType([
|
|
||||||
'name' => 'Query',
|
|
||||||
'fields' => [
|
|
||||||
'test' => [
|
|
||||||
'type' => Type::boolean(),
|
|
||||||
'args' => [
|
|
||||||
'idInput' => Type::id(),
|
|
||||||
'boolInput' => Type::boolean(),
|
|
||||||
'intInput' => Type::int(),
|
|
||||||
'stringInput' => Type::string(),
|
|
||||||
'floatInput' => Type::float()
|
|
||||||
]
|
|
||||||
],
|
|
||||||
]
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return self::$schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getVariableDefinitionNodes()
|
|
||||||
{
|
|
||||||
$idInputDefinition = new VariableDefinitionNode([
|
|
||||||
'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]),
|
|
||||||
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])])
|
|
||||||
]);
|
|
||||||
$boolInputDefinition = new VariableDefinitionNode([
|
|
||||||
'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]),
|
|
||||||
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])])
|
|
||||||
]);
|
|
||||||
$intInputDefinition = new VariableDefinitionNode([
|
|
||||||
'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]),
|
|
||||||
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])])
|
|
||||||
]);
|
|
||||||
$stringInputDefintion = new VariableDefinitionNode([
|
|
||||||
'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]),
|
|
||||||
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])])
|
|
||||||
]);
|
|
||||||
$floatInputDefinition = new VariableDefinitionNode([
|
|
||||||
'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]),
|
|
||||||
'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])])
|
|
||||||
]);
|
|
||||||
return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function runTestCase($variables)
|
|
||||||
{
|
|
||||||
return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Executor;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Type\Schema;
|
use GraphQL\Tests\Executor\TestClasses\ComplexScalar;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function json_encode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute: Handles inputs
|
* Execute: Handles inputs
|
||||||
@ -18,7 +21,6 @@ use PHPUnit\Framework\TestCase;
|
|||||||
*/
|
*/
|
||||||
class VariablesTest extends TestCase
|
class VariablesTest extends TestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function testUsingInlineStructs() : void
|
public function testUsingInlineStructs() : void
|
||||||
{
|
{
|
||||||
// executes with complex input:
|
// executes with complex input:
|
||||||
@ -29,14 +31,12 @@ class VariablesTest extends TestCase
|
|||||||
');
|
');
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
|
||||||
'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// properly parses single value to list:
|
// properly parses single value to list:
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
|
fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ class VariablesTest extends TestCase
|
|||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// properly parses null value to null
|
// properly parses null value to null
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
|
fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ class VariablesTest extends TestCase
|
|||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// properly parses null value in list
|
// properly parses null value in list
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
|
fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
|
||||||
}
|
}
|
||||||
@ -73,12 +73,13 @@ class VariablesTest extends TestCase
|
|||||||
');
|
');
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithObjectInput' => null],
|
'data' => ['fieldWithObjectInput' => null],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
|
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
|
||||||
'path' => ['fieldWithObjectInput'],
|
'path' => ['fieldWithObjectInput'],
|
||||||
'locations' => [['line' => 3, 'column' => 39]]
|
'locations' => [['line' => 3, 'column' => 39]],
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, $result->toArray());
|
$this->assertArraySubset($expected, $result->toArray());
|
||||||
|
|
||||||
@ -94,6 +95,77 @@ class VariablesTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function executeQuery($query, $variableValues = null)
|
||||||
|
{
|
||||||
|
$document = Parser::parse($query);
|
||||||
|
|
||||||
|
return Executor::execute($this->schema(), $document, null, null, $variableValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe: Handles nullable scalars
|
||||||
|
*/
|
||||||
|
public function schema() : Schema
|
||||||
|
{
|
||||||
|
$ComplexScalarType = ComplexScalar::create();
|
||||||
|
|
||||||
|
$TestInputObject = new InputObjectType([
|
||||||
|
'name' => 'TestInputObject',
|
||||||
|
'fields' => [
|
||||||
|
'a' => ['type' => Type::string()],
|
||||||
|
'b' => ['type' => Type::listOf(Type::string())],
|
||||||
|
'c' => ['type' => Type::nonNull(Type::string())],
|
||||||
|
'd' => ['type' => $ComplexScalarType],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$TestNestedInputObject = new InputObjectType([
|
||||||
|
'name' => 'TestNestedInputObject',
|
||||||
|
'fields' => [
|
||||||
|
'na' => ['type' => Type::nonNull($TestInputObject)],
|
||||||
|
'nb' => ['type' => Type::nonNull(Type::string())],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$TestType = new ObjectType([
|
||||||
|
'name' => 'TestType',
|
||||||
|
'fields' => [
|
||||||
|
'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]),
|
||||||
|
'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]),
|
||||||
|
'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]),
|
||||||
|
'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([
|
||||||
|
'type' => Type::string(),
|
||||||
|
'defaultValue' => 'Hello World',
|
||||||
|
]),
|
||||||
|
'fieldWithNestedInputObject' => $this->fieldWithInputArg([
|
||||||
|
'type' => $TestNestedInputObject,
|
||||||
|
'defaultValue' => 'Hello World',
|
||||||
|
]),
|
||||||
|
'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]),
|
||||||
|
'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]),
|
||||||
|
'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]),
|
||||||
|
'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new Schema(['query' => $TestType]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fieldWithInputArg($inputArg)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'args' => ['input' => $inputArg],
|
||||||
|
'resolve' => function ($_, $args) {
|
||||||
|
if (isset($args['input'])) {
|
||||||
|
return json_encode($args['input']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function testUsingVariables() : void
|
public function testUsingVariables() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
@ -119,7 +191,7 @@ class VariablesTest extends TestCase
|
|||||||
');
|
');
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']
|
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
@ -132,63 +204,61 @@ class VariablesTest extends TestCase
|
|||||||
);
|
);
|
||||||
|
|
||||||
// executes with complex scalar input:
|
// executes with complex scalar input:
|
||||||
$params = [ 'input' => [ 'c' => 'foo', 'd' => 'SerializedValue' ] ];
|
$params = ['input' => ['c' => 'foo', 'd' => 'SerializedValue']];
|
||||||
$result = $this->executeQuery($doc, $params);
|
$result = $this->executeQuery($doc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'],
|
||||||
'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// errors on null for nested non-null:
|
// errors on null for nested non-null:
|
||||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
|
||||||
$result = $this->executeQuery($doc, $params);
|
$result = $this->executeQuery($doc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value ' .
|
'Variable "$input" got invalid value ' .
|
||||||
'{"a":"foo","b":"bar","c":null}; ' .
|
'{"a":"foo","b":"bar","c":null}; ' .
|
||||||
'Expected non-nullable type String! not to be null at value.c.',
|
'Expected non-nullable type String! not to be null at value.c.',
|
||||||
'locations' => [['line' => 2, 'column' => 21]],
|
'locations' => [['line' => 2, 'column' => 21]],
|
||||||
'category' => 'graphql'
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// errors on incorrect type:
|
// errors on incorrect type:
|
||||||
$params = [ 'input' => 'foo bar' ];
|
$params = ['input' => 'foo bar'];
|
||||||
$result = $this->executeQuery($doc, $params);
|
$result = $this->executeQuery($doc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value "foo bar"; ' .
|
'Variable "$input" got invalid value "foo bar"; ' .
|
||||||
'Expected type TestInputObject to be an object.',
|
'Expected type TestInputObject to be an object.',
|
||||||
'locations' => [ [ 'line' => 2, 'column' => 21 ] ],
|
'locations' => [['line' => 2, 'column' => 21]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// errors on omission of nested non-null:
|
// errors on omission of nested non-null:
|
||||||
$params = ['input' => ['a' => 'foo', 'b' => 'bar']];
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar']];
|
||||||
|
|
||||||
$result = $this->executeQuery($doc, $params);
|
$result = $this->executeQuery($doc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; '.
|
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' .
|
||||||
'Field value.c of required type String! was not provided.',
|
'Field value.c of required type String! was not provided.',
|
||||||
'locations' => [['line' => 2, 'column' => 21]],
|
'locations' => [['line' => 2, 'column' => 21]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
@ -198,62 +268,59 @@ class VariablesTest extends TestCase
|
|||||||
fieldWithNestedObjectInput(input: $input)
|
fieldWithNestedObjectInput(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$params = [ 'input' => [ 'na' => [ 'a' => 'foo' ] ] ];
|
$params = ['input' => ['na' => ['a' => 'foo']]];
|
||||||
|
|
||||||
$result = $this->executeQuery($nestedDoc, $params);
|
$result = $this->executeQuery($nestedDoc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
||||||
'Field value.na.c of required type String! was not provided.',
|
'Field value.na.c of required type String! was not provided.',
|
||||||
'locations' => [['line' => 2, 'column' => 19]],
|
'locations' => [['line' => 2, 'column' => 19]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
||||||
'Field value.nb of required type String! was not provided.',
|
'Field value.nb of required type String! was not provided.',
|
||||||
'locations' => [['line' => 2, 'column' => 19]],
|
'locations' => [['line' => 2, 'column' => 19]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
|
|
||||||
// errors on addition of unknown input field
|
// errors on addition of unknown input field
|
||||||
$params = ['input' => [ 'a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog' ]];
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog']];
|
||||||
$result = $this->executeQuery($doc, $params);
|
$result = $this->executeQuery($doc, $params);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value ' .
|
'Variable "$input" got invalid value ' .
|
||||||
'{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
|
'{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
|
||||||
'Field "extra" is not defined by type TestInputObject.',
|
'Field "extra" is not defined by type TestInputObject.',
|
||||||
'locations' => [['line' => 2, 'column' => 21]],
|
'locations' => [['line' => 2, 'column' => 21]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: Handles nullable scalars
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('allows nullable inputs to be omitted')
|
* @see it('allows nullable inputs to be omitted')
|
||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeOmitted() : void
|
public function testAllowsNullableInputsToBeOmitted() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithNullableStringInput
|
fieldWithNullableStringInput
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithNullableStringInput' => null]
|
'data' => ['fieldWithNullableStringInput' => null],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
@ -264,7 +331,7 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeOmittedInAVariable() : void
|
public function testAllowsNullableInputsToBeOmittedInAVariable() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
query SetsNullable($value: String) {
|
query SetsNullable($value: String) {
|
||||||
fieldWithNullableStringInput(input: $value)
|
fieldWithNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
@ -279,7 +346,7 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable() : void
|
public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
query SetsNullable {
|
query SetsNullable {
|
||||||
fieldWithNullableStringInput(input: $value)
|
fieldWithNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
@ -288,12 +355,15 @@ class VariablesTest extends TestCase
|
|||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Describe: Handles non-nullable scalars
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('allows nullable inputs to be set to null in a variable')
|
* @see it('allows nullable inputs to be set to null in a variable')
|
||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeSetToNullInAVariable() : void
|
public function testAllowsNullableInputsToBeSetToNullInAVariable() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
query SetsNullable($value: String) {
|
query SetsNullable($value: String) {
|
||||||
fieldWithNullableStringInput(input: $value)
|
fieldWithNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
@ -308,12 +378,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeSetToAValueInAVariable() : void
|
public function testAllowsNullableInputsToBeSetToAValueInAVariable() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query SetsNullable($value: String) {
|
query SetsNullable($value: String) {
|
||||||
fieldWithNullableStringInput(input: $value)
|
fieldWithNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||||
$expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
|
$expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -323,7 +393,7 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNullableInputsToBeSetToAValueDirectly() : void
|
public function testAllowsNullableInputsToBeSetToAValueDirectly() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithNullableStringInput(input: "a")
|
fieldWithNullableStringInput(input: "a")
|
||||||
}
|
}
|
||||||
@ -332,21 +402,18 @@ class VariablesTest extends TestCase
|
|||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Describe: Handles non-nullable scalars
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('allows non-nullable inputs to be omitted given a default')
|
* @see it('allows non-nullable inputs to be omitted given a default')
|
||||||
*/
|
*/
|
||||||
public function testAllowsNonNullableInputsToBeOmittedGivenADefault() : void
|
public function testAllowsNonNullableInputsToBeOmittedGivenADefault() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
query SetsNonNullable($value: String = "default") {
|
query SetsNonNullable($value: String = "default") {
|
||||||
fieldWithNonNullableStringInput(input: $value)
|
fieldWithNonNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithNonNullableStringInput' => '"default"']
|
'data' => ['fieldWithNonNullableStringInput' => '"default"'],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -365,11 +432,11 @@ class VariablesTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Variable "$value" of required type "String!" was not provided.',
|
'message' => 'Variable "$value" of required type "String!" was not provided.',
|
||||||
'locations' => [['line' => 2, 'column' => 31]],
|
'locations' => [['line' => 2, 'column' => 31]],
|
||||||
'category' => 'graphql'
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -379,22 +446,22 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable() : void
|
public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query SetsNonNullable($value: String!) {
|
query SetsNonNullable($value: String!) {
|
||||||
fieldWithNonNullableStringInput(input: $value)
|
fieldWithNonNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['value' => null]);
|
$result = $this->executeQuery($doc, ['value' => null]);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$value" got invalid value null; ' .
|
'Variable "$value" got invalid value null; ' .
|
||||||
'Expected non-nullable type String! not to be null.',
|
'Expected non-nullable type String! not to be null.',
|
||||||
'locations' => [['line' => 2, 'column' => 31]],
|
'locations' => [['line' => 2, 'column' => 31]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -404,12 +471,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNonNullableInputsToBeSetToAValueInAVariable() : void
|
public function testAllowsNonNullableInputsToBeSetToAValueInAVariable() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query SetsNonNullable($value: String!) {
|
query SetsNonNullable($value: String!) {
|
||||||
fieldWithNonNullableStringInput(input: $value)
|
fieldWithNonNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['value' => 'a']);
|
$result = $this->executeQuery($doc, ['value' => 'a']);
|
||||||
$expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
|
$expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -419,7 +486,7 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNonNullableInputsToBeSetToAValueDirectly() : void
|
public function testAllowsNonNullableInputsToBeSetToAValueDirectly() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithNonNullableStringInput(input: "a")
|
fieldWithNonNullableStringInput(input: "a")
|
||||||
}
|
}
|
||||||
@ -433,47 +500,50 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testReportsErrorForMissingNonNullableInputs() : void
|
public function testReportsErrorForMissingNonNullableInputs() : void
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithNonNullableStringInput
|
fieldWithNonNullableStringInput
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' => 'Argument "input" of required type "String!" was not provided.',
|
'message' => 'Argument "input" of required type "String!" was not provided.',
|
||||||
'locations' => [ [ 'line' => 3, 'column' => 9 ] ],
|
'locations' => [['line' => 3, 'column' => 9]],
|
||||||
'path' => [ 'fieldWithNonNullableStringInput' ],
|
'path' => ['fieldWithNonNullableStringInput'],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: Handles lists and nullability
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('reports error for array passed into string input')
|
* @see it('reports error for array passed into string input')
|
||||||
*/
|
*/
|
||||||
public function testReportsErrorForArrayPassedIntoStringInput() : void
|
public function testReportsErrorForArrayPassedIntoStringInput() : void
|
||||||
{
|
{
|
||||||
|
$doc = '
|
||||||
$doc = '
|
|
||||||
query SetsNonNullable($value: String!) {
|
query SetsNonNullable($value: String!) {
|
||||||
fieldWithNonNullableStringInput(input: $value)
|
fieldWithNonNullableStringInput(input: $value)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$variables = ['value' => [1, 2, 3]];
|
$variables = ['value' => [1, 2, 3]];
|
||||||
$result = $this->executeQuery($doc, $variables);
|
$result = $this->executeQuery($doc, $variables);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$value" got invalid value [1,2,3]; Expected type ' .
|
'Variable "$value" got invalid value [1,2,3]; Expected type ' .
|
||||||
'String; String cannot represent an array value: [1,2,3]',
|
'String; String cannot represent an array value: [1,2,3]',
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
'locations' => [
|
'locations' => [
|
||||||
['line' => 2, 'column' => 31]
|
['line' => 2, 'column' => 31],
|
||||||
]
|
],
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -498,38 +568,37 @@ class VariablesTest extends TestCase
|
|||||||
// and are being run against a new schema which have introduced a breaking
|
// and are being run against a new schema which have introduced a breaking
|
||||||
// change to make a formerly non-required argument required, this asserts
|
// change to make a formerly non-required argument required, this asserts
|
||||||
// failure before allowing the underlying code to receive a non-null value.
|
// failure before allowing the underlying code to receive a non-null value.
|
||||||
$result = $this->executeQuery('
|
$result = $this->executeQuery('
|
||||||
{
|
{
|
||||||
fieldWithNonNullableStringInput(input: $foo)
|
fieldWithNonNullableStringInput(input: $foo)
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithNonNullableStringInput' => null],
|
'data' => ['fieldWithNonNullableStringInput' => null],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Argument "input" of required type "String!" was provided the ' .
|
'Argument "input" of required type "String!" was provided the ' .
|
||||||
'variable "$foo" which was not provided a runtime value.',
|
'variable "$foo" which was not provided a runtime value.',
|
||||||
'locations' => [['line' => 3, 'column' => 48]],
|
'locations' => [['line' => 3, 'column' => 48]],
|
||||||
'path' => ['fieldWithNonNullableStringInput'],
|
'path' => ['fieldWithNonNullableStringInput'],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: Handles lists and nullability
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('allows lists to be null')
|
* @see it('allows lists to be null')
|
||||||
*/
|
*/
|
||||||
public function testAllowsListsToBeNull() : void
|
public function testAllowsListsToBeNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]) {
|
query q($input:[String]) {
|
||||||
list(input: $input)
|
list(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => null]);
|
$result = $this->executeQuery($doc, ['input' => null]);
|
||||||
$expected = ['data' => ['list' => null]];
|
$expected = ['data' => ['list' => null]];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
@ -540,12 +609,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsListsToContainValues() : void
|
public function testAllowsListsToContainValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]) {
|
query q($input:[String]) {
|
||||||
list(input: $input)
|
list(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||||
$expected = ['data' => ['list' => '["A"]']];
|
$expected = ['data' => ['list' => '["A"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -555,12 +624,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsListsToContainNull() : void
|
public function testAllowsListsToContainNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]) {
|
query q($input:[String]) {
|
||||||
list(input: $input)
|
list(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
|
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||||
$expected = ['data' => ['list' => '["A",null,"B"]']];
|
$expected = ['data' => ['list' => '["A",null,"B"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -570,22 +639,22 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowNonNullListsToBeNull() : void
|
public function testDoesNotAllowNonNullListsToBeNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]!) {
|
query q($input:[String]!) {
|
||||||
nnList(input: $input)
|
nnList(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => null]);
|
$result = $this->executeQuery($doc, ['input' => null]);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value null; ' .
|
'Variable "$input" got invalid value null; ' .
|
||||||
'Expected non-nullable type [String]! not to be null.',
|
'Expected non-nullable type [String]! not to be null.',
|
||||||
'locations' => [['line' => 2, 'column' => 17]],
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -595,12 +664,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNonNullListsToContainValues() : void
|
public function testAllowsNonNullListsToContainValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]!) {
|
query q($input:[String]!) {
|
||||||
nnList(input: $input)
|
nnList(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||||
$expected = ['data' => ['nnList' => '["A"]']];
|
$expected = ['data' => ['nnList' => '["A"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -610,12 +679,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNonNullListsToContainNull() : void
|
public function testAllowsNonNullListsToContainNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String]!) {
|
query q($input:[String]!) {
|
||||||
nnList(input: $input)
|
nnList(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A',null,'B']]);
|
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||||
$expected = ['data' => ['nnList' => '["A",null,"B"]']];
|
$expected = ['data' => ['nnList' => '["A",null,"B"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -625,12 +694,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsListsOfNonNullsToBeNull() : void
|
public function testAllowsListsOfNonNullsToBeNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]) {
|
query q($input:[String!]) {
|
||||||
listNN(input: $input)
|
listNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => null]);
|
$result = $this->executeQuery($doc, ['input' => null]);
|
||||||
$expected = ['data' => ['listNN' => null]];
|
$expected = ['data' => ['listNN' => null]];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -640,12 +709,12 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsListsOfNonNullsToContainValues() : void
|
public function testAllowsListsOfNonNullsToContainValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]) {
|
query q($input:[String!]) {
|
||||||
listNN(input: $input)
|
listNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||||
$expected = ['data' => ['listNN' => '["A"]']];
|
$expected = ['data' => ['listNN' => '["A"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -655,22 +724,22 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowListsOfNonNullsToContainNull() : void
|
public function testDoesNotAllowListsOfNonNullsToContainNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]) {
|
query q($input:[String!]) {
|
||||||
listNN(input: $input)
|
listNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
||||||
'Expected non-nullable type String! not to be null at value[1].',
|
'Expected non-nullable type String! not to be null at value[1].',
|
||||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -680,22 +749,22 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowNonNullListsOfNonNullsToBeNull() : void
|
public function testDoesNotAllowNonNullListsOfNonNullsToBeNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]!) {
|
query q($input:[String!]!) {
|
||||||
nnListNN(input: $input)
|
nnListNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => null]);
|
$result = $this->executeQuery($doc, ['input' => null]);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value null; ' .
|
'Variable "$input" got invalid value null; ' .
|
||||||
'Expected non-nullable type [String!]! not to be null.',
|
'Expected non-nullable type [String!]! not to be null.',
|
||||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -705,37 +774,39 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsNonNullListsOfNonNullsToContainValues() : void
|
public function testAllowsNonNullListsOfNonNullsToContainValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]!) {
|
query q($input:[String!]!) {
|
||||||
nnListNN(input: $input)
|
nnListNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
$result = $this->executeQuery($doc, ['input' => ['A']]);
|
||||||
$expected = ['data' => ['nnListNN' => '["A"]']];
|
$expected = ['data' => ['nnListNN' => '["A"]']];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: Execute: Uses argument default values
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('does not allow non-null lists of non-nulls to contain null')
|
* @see it('does not allow non-null lists of non-nulls to contain null')
|
||||||
*/
|
*/
|
||||||
public function testDoesNotAllowNonNullListsOfNonNullsToContainNull() : void
|
public function testDoesNotAllowNonNullListsOfNonNullsToContainNull() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input:[String!]!) {
|
query q($input:[String!]!) {
|
||||||
nnListNN(input: $input)
|
nnListNN(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
$result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
||||||
'Expected non-nullable type String! not to be null at value[1].',
|
'Expected non-nullable type String! not to be null at value[1].',
|
||||||
'locations' => [ ['line' => 2, 'column' => 17] ],
|
'locations' => [['line' => 2, 'column' => 17]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -745,23 +816,23 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowInvalidTypesToBeUsedAsValues() : void
|
public function testDoesNotAllowInvalidTypesToBeUsedAsValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input: TestType!) {
|
query q($input: TestType!) {
|
||||||
fieldWithObjectInput(input: $input)
|
fieldWithObjectInput(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$vars = [ 'input' => [ 'list' => [ 'A', 'B' ] ] ];
|
$vars = ['input' => ['list' => ['A', 'B']]];
|
||||||
$result = $this->executeQuery($doc, $vars);
|
$result = $this->executeQuery($doc, $vars);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" expected value of type "TestType!" which cannot ' .
|
'Variable "$input" expected value of type "TestType!" which cannot ' .
|
||||||
'be used as an input type.',
|
'be used as an input type.',
|
||||||
'locations' => [['line' => 2, 'column' => 25]],
|
'locations' => [['line' => 2, 'column' => 25]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -771,29 +842,28 @@ class VariablesTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotAllowUnknownTypesToBeUsedAsValues() : void
|
public function testDoesNotAllowUnknownTypesToBeUsedAsValues() : void
|
||||||
{
|
{
|
||||||
$doc = '
|
$doc = '
|
||||||
query q($input: UnknownType!) {
|
query q($input: UnknownType!) {
|
||||||
fieldWithObjectInput(input: $input)
|
fieldWithObjectInput(input: $input)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$vars = ['input' => 'whoknows'];
|
$vars = ['input' => 'whoknows'];
|
||||||
|
|
||||||
$result = $this->executeQuery($doc, $vars);
|
$result = $this->executeQuery($doc, $vars);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Variable "$input" expected value of type "UnknownType!" which ' .
|
'Variable "$input" expected value of type "UnknownType!" which ' .
|
||||||
'cannot be used as an input type.',
|
'cannot be used as an input type.',
|
||||||
'locations' => [['line' => 2, 'column' => 25]],
|
'locations' => [['line' => 2, 'column' => 25]],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: Execute: Uses argument default values
|
|
||||||
/**
|
/**
|
||||||
* @see it('when no argument provided')
|
* @see it('when no argument provided')
|
||||||
*/
|
*/
|
||||||
@ -834,84 +904,17 @@ class VariablesTest extends TestCase
|
|||||||
}');
|
}');
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => ['fieldWithDefaultArgumentValue' => null],
|
'data' => ['fieldWithDefaultArgumentValue' => null],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' =>
|
'message' =>
|
||||||
'Argument "input" has invalid value WRONG_TYPE.',
|
'Argument "input" has invalid value WRONG_TYPE.',
|
||||||
'locations' => [ [ 'line' => 2, 'column' => 50 ] ],
|
'locations' => [['line' => 2, 'column' => 50]],
|
||||||
'path' => [ 'fieldWithDefaultArgumentValue' ],
|
'path' => ['fieldWithDefaultArgumentValue'],
|
||||||
'category' => 'graphql',
|
'category' => 'graphql',
|
||||||
]]
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function schema()
|
|
||||||
{
|
|
||||||
$ComplexScalarType = ComplexScalar::create();
|
|
||||||
|
|
||||||
$TestInputObject = new InputObjectType([
|
|
||||||
'name' => 'TestInputObject',
|
|
||||||
'fields' => [
|
|
||||||
'a' => ['type' => Type::string()],
|
|
||||||
'b' => ['type' => Type::listOf(Type::string())],
|
|
||||||
'c' => ['type' => Type::nonNull(Type::string())],
|
|
||||||
'd' => ['type' => $ComplexScalarType],
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
$TestNestedInputObject = new InputObjectType([
|
|
||||||
'name' => 'TestNestedInputObject',
|
|
||||||
'fields' => [
|
|
||||||
'na' => [ 'type' => Type::nonNull($TestInputObject) ],
|
|
||||||
'nb' => [ 'type' => Type::nonNull(Type::string()) ],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$TestType = new ObjectType([
|
|
||||||
'name' => 'TestType',
|
|
||||||
'fields' => [
|
|
||||||
'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]),
|
|
||||||
'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]),
|
|
||||||
'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]),
|
|
||||||
'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([
|
|
||||||
'type' => Type::string(),
|
|
||||||
'defaultValue' => 'Hello World',
|
|
||||||
]),
|
|
||||||
'fieldWithNestedInputObject' => $this->fieldWithInputArg([
|
|
||||||
'type' => $TestNestedInputObject,
|
|
||||||
'defaultValue' => 'Hello World'
|
|
||||||
]),
|
|
||||||
'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]),
|
|
||||||
'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]),
|
|
||||||
'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]),
|
|
||||||
'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]),
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
$schema = new Schema(['query' => $TestType]);
|
|
||||||
return $schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function fieldWithInputArg($inputArg)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'type' => Type::string(),
|
|
||||||
'args' => ['input' => $inputArg],
|
|
||||||
'resolve' => function ($_, $args) {
|
|
||||||
if (isset($args['input'])) {
|
|
||||||
return json_encode($args['input']);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeQuery($query, $variableValues = null)
|
|
||||||
{
|
|
||||||
$document = Parser::parse($query);
|
|
||||||
return Executor::execute($this->schema(), $document, null, null, $variableValues);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Language;
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
|
use GraphQL\Error\SyntaxError;
|
||||||
use GraphQL\Language\Lexer;
|
use GraphQL\Language\Lexer;
|
||||||
use GraphQL\Language\Source;
|
use GraphQL\Language\Source;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Language\SourceLocation;
|
||||||
use GraphQL\Language\Token;
|
use GraphQL\Language\Token;
|
||||||
use GraphQL\Error\SyntaxError;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function count;
|
||||||
|
use function json_decode;
|
||||||
|
|
||||||
class LexerTest extends TestCase
|
class LexerTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -23,17 +28,45 @@ class LexerTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function expectSyntaxError($text, $message, $location)
|
||||||
|
{
|
||||||
|
$this->expectException(SyntaxError::class);
|
||||||
|
$this->expectExceptionMessage($message);
|
||||||
|
try {
|
||||||
|
$this->lexOne($text);
|
||||||
|
} catch (SyntaxError $error) {
|
||||||
|
$this->assertEquals([$location], $error->getLocations());
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $body
|
||||||
|
* @return Token
|
||||||
|
*/
|
||||||
|
private function lexOne($body)
|
||||||
|
{
|
||||||
|
$lexer = new Lexer(new Source($body));
|
||||||
|
|
||||||
|
return $lexer->advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loc($line, $column)
|
||||||
|
{
|
||||||
|
return new SourceLocation($line, $column);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('accepts BOM header')
|
* @see it('accepts BOM header')
|
||||||
*/
|
*/
|
||||||
public function testAcceptsBomHeader() : void
|
public function testAcceptsBomHeader() : void
|
||||||
{
|
{
|
||||||
$bom = Utils::chr(0xFEFF);
|
$bom = Utils::chr(0xFEFF);
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 2,
|
'start' => 2,
|
||||||
'end' => 5,
|
'end' => 5,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, (array) $this->lexOne($bom . ' foo'));
|
$this->assertArraySubset($expected, (array) $this->lexOne($bom . ' foo'));
|
||||||
@ -45,12 +78,12 @@ class LexerTest extends TestCase
|
|||||||
public function testRecordsLineAndColumn() : void
|
public function testRecordsLineAndColumn() : void
|
||||||
{
|
{
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 8,
|
'start' => 8,
|
||||||
'end' => 11,
|
'end' => 11,
|
||||||
'line' => 4,
|
'line' => 4,
|
||||||
'column' => 3,
|
'column' => 3,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, (array) $this->lexOne("\n \r\n \r foo\n"));
|
$this->assertArraySubset($expected, (array) $this->lexOne("\n \r\n \r foo\n"));
|
||||||
}
|
}
|
||||||
@ -67,10 +100,10 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 6,
|
'start' => 6,
|
||||||
'end' => 9,
|
'end' => 9,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, (array) $this->lexOne($example1));
|
$this->assertArraySubset($expected, (array) $this->lexOne($example1));
|
||||||
|
|
||||||
@ -80,18 +113,18 @@ class LexerTest extends TestCase
|
|||||||
';
|
';
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 18,
|
'start' => 18,
|
||||||
'end' => 21,
|
'end' => 21,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, (array) $this->lexOne($example2));
|
$this->assertArraySubset($expected, (array) $this->lexOne($example2));
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => Token::NAME,
|
'kind' => Token::NAME,
|
||||||
'start' => 3,
|
'start' => 3,
|
||||||
'end' => 6,
|
'end' => 6,
|
||||||
'value' => 'foo'
|
'value' => 'foo',
|
||||||
];
|
];
|
||||||
|
|
||||||
$example3 = ',,,foo,,,';
|
$example3 = ',,,foo,,,';
|
||||||
@ -131,7 +164,7 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testUpdatesLineNumbersInErrorForFileContext() : void
|
public function testUpdatesLineNumbersInErrorForFileContext() : void
|
||||||
{
|
{
|
||||||
$str = '' .
|
$str = '' .
|
||||||
"\n" .
|
"\n" .
|
||||||
"\n" .
|
"\n" .
|
||||||
" ?\n" .
|
" ?\n" .
|
||||||
@ -181,63 +214,86 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testLexesStrings() : void
|
public function testLexesStrings() : void
|
||||||
{
|
{
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 8,
|
'start' => 0,
|
||||||
'value' => 'simple'
|
'end' => 8,
|
||||||
], (array) $this->lexOne('"simple"'));
|
'value' => 'simple',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"simple"')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertArraySubset(
|
||||||
|
[
|
||||||
|
'kind' => Token::STRING,
|
||||||
|
'start' => 0,
|
||||||
|
'end' => 15,
|
||||||
|
'value' => ' white space ',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('" white space "')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 15,
|
'start' => 0,
|
||||||
'value' => ' white space '
|
'end' => 10,
|
||||||
], (array) $this->lexOne('" white space "'));
|
'value' => 'quote "',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"quote \\""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 10,
|
'start' => 0,
|
||||||
'value' => 'quote "'
|
'end' => 25,
|
||||||
], (array) $this->lexOne('"quote \\""'));
|
'value' => 'escaped \n\r\b\t\f',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 25,
|
'start' => 0,
|
||||||
'value' => 'escaped \n\r\b\t\f'
|
'end' => 16,
|
||||||
], (array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"'));
|
'value' => 'slashes \\ \/',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"slashes \\\\ \\\\/"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 16,
|
'start' => 0,
|
||||||
'value' => 'slashes \\ \/'
|
'end' => 13,
|
||||||
], (array) $this->lexOne('"slashes \\\\ \\\\/"'));
|
'value' => 'unicode яуц',
|
||||||
|
],
|
||||||
$this->assertArraySubset([
|
(array) $this->lexOne('"unicode яуц"')
|
||||||
'kind' => Token::STRING,
|
);
|
||||||
'start' => 0,
|
|
||||||
'end' => 13,
|
|
||||||
'value' => 'unicode яуц'
|
|
||||||
], (array) $this->lexOne('"unicode яуц"'));
|
|
||||||
|
|
||||||
$unicode = json_decode('"\u1234\u5678\u90AB\uCDEF"');
|
$unicode = json_decode('"\u1234\u5678\u90AB\uCDEF"');
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 34,
|
'start' => 0,
|
||||||
'value' => 'unicode ' . $unicode
|
'end' => 34,
|
||||||
], (array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"'));
|
'value' => 'unicode ' . $unicode,
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::STRING,
|
||||||
'end' => 26,
|
'start' => 0,
|
||||||
'value' => $unicode
|
'end' => 26,
|
||||||
], (array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"'));
|
'value' => $unicode,
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,86 +301,128 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testLexesBlockString() : void
|
public function testLexesBlockString() : void
|
||||||
{
|
{
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 12,
|
'start' => 0,
|
||||||
'value' => 'simple'
|
'end' => 12,
|
||||||
], (array) $this->lexOne('"""simple"""'));
|
'value' => 'simple',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""simple"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 19,
|
'start' => 0,
|
||||||
'value' => ' white space '
|
'end' => 19,
|
||||||
], (array) $this->lexOne('""" white space """'));
|
'value' => ' white space ',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('""" white space """')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 22,
|
'start' => 0,
|
||||||
'value' => 'contains " quote'
|
'end' => 22,
|
||||||
], (array) $this->lexOne('"""contains " quote"""'));
|
'value' => 'contains " quote',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""contains " quote"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 31,
|
'start' => 0,
|
||||||
'value' => 'contains """ triplequote'
|
'end' => 31,
|
||||||
], (array) $this->lexOne('"""contains \\""" triplequote"""'));
|
'value' => 'contains """ triplequote',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""contains \\""" triplequote"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 16,
|
'start' => 0,
|
||||||
'value' => "multi\nline"
|
'end' => 16,
|
||||||
], (array) $this->lexOne("\"\"\"multi\nline\"\"\""));
|
'value' => "multi\nline",
|
||||||
|
],
|
||||||
|
(array) $this->lexOne("\"\"\"multi\nline\"\"\"")
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 28,
|
'start' => 0,
|
||||||
'value' => "multi\nline\nnormalized"
|
'end' => 28,
|
||||||
], (array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\""));
|
'value' => "multi\nline\nnormalized",
|
||||||
|
],
|
||||||
|
(array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"")
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 32,
|
'start' => 0,
|
||||||
'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234'
|
'end' => 32,
|
||||||
], (array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""'));
|
'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 19,
|
'start' => 0,
|
||||||
'value' => 'slashes \\\\ \\/'
|
'end' => 19,
|
||||||
], (array) $this->lexOne('"""slashes \\\\ \\/"""'));
|
'value' => 'slashes \\\\ \\/',
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""slashes \\\\ \\/"""')
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'kind' => Token::BLOCK_STRING,
|
[
|
||||||
'start' => 0,
|
'kind' => Token::BLOCK_STRING,
|
||||||
'end' => 68,
|
'start' => 0,
|
||||||
'value' => "spans\n multiple\n lines"
|
'end' => 68,
|
||||||
], (array) $this->lexOne("\"\"\"
|
'value' => "spans\n multiple\n lines",
|
||||||
|
],
|
||||||
|
(array) $this->lexOne('"""
|
||||||
|
|
||||||
spans
|
spans
|
||||||
multiple
|
multiple
|
||||||
lines
|
lines
|
||||||
|
|
||||||
\"\"\""));
|
"""')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reportsUsefulStringErrors() {
|
public function reportsUsefulStringErrors()
|
||||||
|
{
|
||||||
return [
|
return [
|
||||||
['"', "Unterminated string.", $this->loc(1, 2)],
|
['"', 'Unterminated string.', $this->loc(1, 2)],
|
||||||
['"no end quote', "Unterminated string.", $this->loc(1, 14)],
|
['"no end quote', 'Unterminated string.', $this->loc(1, 14)],
|
||||||
["'single quotes'", "Unexpected single quote character ('), did you mean to use a double quote (\")?", $this->loc(1, 1)],
|
[
|
||||||
['"contains unescaped \u0007 control char"', "Invalid character within String: \"\\u0007\"", $this->loc(1, 21)],
|
"'single quotes'",
|
||||||
|
"Unexpected single quote character ('), did you mean to use a double quote (\")?",
|
||||||
|
$this->loc(
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"contains unescaped \u0007 control char"',
|
||||||
|
"Invalid character within String: \"\\u0007\"",
|
||||||
|
$this->loc(
|
||||||
|
1,
|
||||||
|
21
|
||||||
|
),
|
||||||
|
],
|
||||||
['"null-byte is not \u0000 end of file"', 'Invalid character within String: "\\u0000"', $this->loc(1, 19)],
|
['"null-byte is not \u0000 end of file"', 'Invalid character within String: "\\u0000"', $this->loc(1, 19)],
|
||||||
['"multi' . "\n" . 'line"', "Unterminated string.", $this->loc(1, 7)],
|
['"multi' . "\n" . 'line"', 'Unterminated string.', $this->loc(1, 7)],
|
||||||
['"multi' . "\r" . 'line"', "Unterminated string.", $this->loc(1, 7)],
|
['"multi' . "\r" . 'line"', 'Unterminated string.', $this->loc(1, 7)],
|
||||||
['"bad \\z esc"', "Invalid character escape sequence: \\z", $this->loc(1, 7)],
|
['"bad \\z esc"', 'Invalid character escape sequence: \\z', $this->loc(1, 7)],
|
||||||
['"bad \\x esc"', "Invalid character escape sequence: \\x", $this->loc(1, 7)],
|
['"bad \\x esc"', "Invalid character escape sequence: \\x", $this->loc(1, 7)],
|
||||||
['"bad \\u1 esc"', "Invalid character escape sequence: \\u1 es", $this->loc(1, 7)],
|
['"bad \\u1 esc"', "Invalid character escape sequence: \\u1 es", $this->loc(1, 7)],
|
||||||
['"bad \\u0XX1 esc"', "Invalid character escape sequence: \\u0XX1", $this->loc(1, 7)],
|
['"bad \\u0XX1 esc"', "Invalid character escape sequence: \\u0XX1", $this->loc(1, 7)],
|
||||||
@ -336,25 +434,40 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulStringErrors
|
* @dataProvider reportsUsefulStringErrors
|
||||||
* @see it('lex reports useful string errors')
|
* @see it('lex reports useful string errors')
|
||||||
*/
|
*/
|
||||||
public function testLexReportsUsefulStringErrors($str, $expectedMessage, $location) : void
|
public function testLexReportsUsefulStringErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
$this->expectSyntaxError($str, $expectedMessage, $location);
|
$this->expectSyntaxError($str, $expectedMessage, $location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reportsUsefulBlockStringErrors() {
|
public function reportsUsefulBlockStringErrors()
|
||||||
|
{
|
||||||
return [
|
return [
|
||||||
['"""', "Unterminated string.", $this->loc(1, 4)],
|
['"""', 'Unterminated string.', $this->loc(1, 4)],
|
||||||
['"""no end quote', "Unterminated string.", $this->loc(1, 16)],
|
['"""no end quote', 'Unterminated string.', $this->loc(1, 16)],
|
||||||
['"""contains unescaped ' . json_decode('"\u0007"') . ' control char"""', "Invalid character within String: \"\\u0007\"", $this->loc(1, 23)],
|
[
|
||||||
['"""null-byte is not ' . json_decode('"\u0000"') . ' end of file"""', "Invalid character within String: \"\\u0000\"", $this->loc(1, 21)],
|
'"""contains unescaped ' . json_decode('"\u0007"') . ' control char"""',
|
||||||
|
"Invalid character within String: \"\\u0007\"",
|
||||||
|
$this->loc(
|
||||||
|
1,
|
||||||
|
23
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'"""null-byte is not ' . json_decode('"\u0000"') . ' end of file"""',
|
||||||
|
"Invalid character within String: \"\\u0000\"",
|
||||||
|
$this->loc(
|
||||||
|
1,
|
||||||
|
21
|
||||||
|
),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulBlockStringErrors
|
* @dataProvider reportsUsefulBlockStringErrors
|
||||||
* @see it('lex reports useful block string errors')
|
* @see it('lex reports useful block string errors')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulBlockStringErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulBlockStringErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -435,21 +548,21 @@ class LexerTest extends TestCase
|
|||||||
public function reportsUsefulNumberErrors()
|
public function reportsUsefulNumberErrors()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[ '00', "Invalid number, unexpected digit after 0: \"0\"", $this->loc(1, 2)],
|
['00', 'Invalid number, unexpected digit after 0: "0"', $this->loc(1, 2)],
|
||||||
[ '+1', "Cannot parse the unexpected character \"+\".", $this->loc(1, 1)],
|
['+1', 'Cannot parse the unexpected character "+".', $this->loc(1, 1)],
|
||||||
[ '1.', "Invalid number, expected digit but got: <EOF>", $this->loc(1, 3)],
|
['1.', 'Invalid number, expected digit but got: <EOF>', $this->loc(1, 3)],
|
||||||
[ '1.e1', "Invalid number, expected digit but got: \"e\"", $this->loc(1, 3)],
|
['1.e1', 'Invalid number, expected digit but got: "e"', $this->loc(1, 3)],
|
||||||
[ '.123', "Cannot parse the unexpected character \".\".", $this->loc(1, 1)],
|
['.123', 'Cannot parse the unexpected character ".".', $this->loc(1, 1)],
|
||||||
[ '1.A', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 3)],
|
['1.A', 'Invalid number, expected digit but got: "A"', $this->loc(1, 3)],
|
||||||
[ '-A', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 2)],
|
['-A', 'Invalid number, expected digit but got: "A"', $this->loc(1, 2)],
|
||||||
[ '1.0e', "Invalid number, expected digit but got: <EOF>", $this->loc(1, 5)],
|
['1.0e', 'Invalid number, expected digit but got: <EOF>', $this->loc(1, 5)],
|
||||||
[ '1.0eA', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 5)],
|
['1.0eA', 'Invalid number, expected digit but got: "A"', $this->loc(1, 5)],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulNumberErrors
|
* @dataProvider reportsUsefulNumberErrors
|
||||||
* @see it('lex reports useful number errors')
|
* @see it('lex reports useful number errors')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulNumberErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulNumberErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -521,8 +634,8 @@ class LexerTest extends TestCase
|
|||||||
$unicode2 = json_decode('"\u200b"');
|
$unicode2 = json_decode('"\u200b"');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
['..', "Cannot parse the unexpected character \".\".", $this->loc(1, 1)],
|
['..', 'Cannot parse the unexpected character ".".', $this->loc(1, 1)],
|
||||||
['?', "Cannot parse the unexpected character \"?\".", $this->loc(1, 1)],
|
['?', 'Cannot parse the unexpected character "?".', $this->loc(1, 1)],
|
||||||
[$unicode1, "Cannot parse the unexpected character \"\\u203b\".", $this->loc(1, 1)],
|
[$unicode1, "Cannot parse the unexpected character \"\\u203b\".", $this->loc(1, 1)],
|
||||||
[$unicode2, "Cannot parse the unexpected character \"\\u200b\".", $this->loc(1, 1)],
|
[$unicode2, "Cannot parse the unexpected character \"\\u200b\".", $this->loc(1, 1)],
|
||||||
];
|
];
|
||||||
@ -530,7 +643,7 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider reportsUsefulUnknownCharErrors
|
* @dataProvider reportsUsefulUnknownCharErrors
|
||||||
* @see it('lex reports useful unknown character error')
|
* @see it('lex reports useful unknown character error')
|
||||||
*/
|
*/
|
||||||
public function testReportsUsefulUnknownCharErrors($str, $expectedMessage, $location) : void
|
public function testReportsUsefulUnknownCharErrors($str, $expectedMessage, $location) : void
|
||||||
{
|
{
|
||||||
@ -542,17 +655,20 @@ class LexerTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testReportsUsefulDashesInfo() : void
|
public function testReportsUsefulDashesInfo() : void
|
||||||
{
|
{
|
||||||
$q = 'a-b';
|
$q = 'a-b';
|
||||||
$lexer = new Lexer(new Source($q));
|
$lexer = new Lexer(new Source($q));
|
||||||
$this->assertArraySubset(['kind' => Token::NAME, 'start' => 0, 'end' => 1, 'value' => 'a'], (array) $lexer->advance());
|
$this->assertArraySubset(
|
||||||
|
['kind' => Token::NAME, 'start' => 0, 'end' => 1, 'value' => 'a'],
|
||||||
|
(array) $lexer->advance()
|
||||||
|
);
|
||||||
|
|
||||||
$this->expectException(SyntaxError::class);
|
$this->expectException(SyntaxError::class);
|
||||||
$this->expectExceptionMessage('Syntax Error: Invalid number, expected digit but got: "b"');
|
$this->expectExceptionMessage('Syntax Error: Invalid number, expected digit but got: "b"');
|
||||||
try {
|
try {
|
||||||
$lexer->advance();
|
$lexer->advance();
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
} catch(SyntaxError $error) {
|
} catch (SyntaxError $error) {
|
||||||
$this->assertEquals([$this->loc(1,3)], $error->getLocations());
|
$this->assertEquals([$this->loc(1, 3)], $error->getLocations());
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,49 +696,28 @@ class LexerTest extends TestCase
|
|||||||
|
|
||||||
$tokens = [];
|
$tokens = [];
|
||||||
for ($tok = $startToken; $tok; $tok = $tok->next) {
|
for ($tok = $startToken; $tok; $tok = $tok->next) {
|
||||||
if (!empty($tokens)) {
|
if (! empty($tokens)) {
|
||||||
// Tokens are double-linked, prev should point to last seen token.
|
// Tokens are double-linked, prev should point to last seen token.
|
||||||
$this->assertSame($tokens[count($tokens) - 1], $tok->prev);
|
$this->assertSame($tokens[count($tokens) - 1], $tok->prev);
|
||||||
}
|
}
|
||||||
$tokens[] = $tok;
|
$tokens[] = $tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'<SOF>',
|
[
|
||||||
'{',
|
'<SOF>',
|
||||||
'Comment',
|
'{',
|
||||||
'Name',
|
'Comment',
|
||||||
'}',
|
'Name',
|
||||||
'<EOF>'
|
'}',
|
||||||
], Utils::map($tokens, function ($tok) {
|
'<EOF>',
|
||||||
return $tok->kind;
|
],
|
||||||
}));
|
Utils::map(
|
||||||
}
|
$tokens,
|
||||||
|
function ($tok) {
|
||||||
/**
|
return $tok->kind;
|
||||||
* @param string $body
|
}
|
||||||
* @return Token
|
)
|
||||||
*/
|
);
|
||||||
private function lexOne($body)
|
|
||||||
{
|
|
||||||
$lexer = new Lexer(new Source($body));
|
|
||||||
return $lexer->advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loc($line, $column)
|
|
||||||
{
|
|
||||||
return new SourceLocation($line, $column);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function expectSyntaxError($text, $message, $location)
|
|
||||||
{
|
|
||||||
$this->expectException(SyntaxError::class);
|
|
||||||
$this->expectExceptionMessage($message);
|
|
||||||
try {
|
|
||||||
$this->lexOne($text);
|
|
||||||
} catch (SyntaxError $error) {
|
|
||||||
$this->assertEquals([$location], $error->getLocations());
|
|
||||||
throw $error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Language;
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
|
use GraphQL\Error\SyntaxError;
|
||||||
use GraphQL\Language\AST\ArgumentNode;
|
use GraphQL\Language\AST\ArgumentNode;
|
||||||
use GraphQL\Language\AST\FieldNode;
|
use GraphQL\Language\AST\FieldNode;
|
||||||
use GraphQL\Language\AST\NameNode;
|
use GraphQL\Language\AST\NameNode;
|
||||||
@ -13,9 +17,10 @@ use GraphQL\Language\AST\StringValueNode;
|
|||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\Source;
|
use GraphQL\Language\Source;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Language\SourceLocation;
|
||||||
use GraphQL\Error\SyntaxError;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class ParserTest extends TestCase
|
class ParserTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -43,22 +48,40 @@ class ParserTest extends TestCase
|
|||||||
public function parseProvidesUsefulErrors()
|
public function parseProvidesUsefulErrors()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['{', "Syntax Error: Expected Name, found <EOF>", "Syntax Error: Expected Name, found <EOF>\n\nGraphQL request (1:2)\n1: {\n ^\n", [1], [new SourceLocation(1, 2)]],
|
[
|
||||||
['{ ...MissingOn }
|
'{',
|
||||||
|
'Syntax Error: Expected Name, found <EOF>',
|
||||||
|
"Syntax Error: Expected Name, found <EOF>\n\nGraphQL request (1:2)\n1: {\n ^\n",
|
||||||
|
[1],
|
||||||
|
[new SourceLocation(
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'{ ...MissingOn }
|
||||||
fragment MissingOn Type
|
fragment MissingOn Type
|
||||||
', "Syntax Error: Expected \"on\", found Name \"Type\"", "Syntax Error: Expected \"on\", found Name \"Type\"\n\nGraphQL request (2:20)\n1: { ...MissingOn }\n2: fragment MissingOn Type\n ^\n3: \n",],
|
', 'Syntax Error: Expected "on", found Name "Type"',
|
||||||
['{ field: {} }', "Syntax Error: Expected Name, found {", "Syntax Error: Expected Name, found {\n\nGraphQL request (1:10)\n1: { field: {} }\n ^\n"],
|
"Syntax Error: Expected \"on\", found Name \"Type\"\n\nGraphQL request (2:20)\n1: { ...MissingOn }\n2: fragment MissingOn Type\n ^\n3: \n",
|
||||||
['notanoperation Foo { field }', "Syntax Error: Unexpected Name \"notanoperation\"", "Syntax Error: Unexpected Name \"notanoperation\"\n\nGraphQL request (1:1)\n1: notanoperation Foo { field }\n ^\n"],
|
],
|
||||||
['...', "Syntax Error: Unexpected ...", "Syntax Error: Unexpected ...\n\nGraphQL request (1:1)\n1: ...\n ^\n"],
|
['{ field: {} }', 'Syntax Error: Expected Name, found {', "Syntax Error: Expected Name, found {\n\nGraphQL request (1:10)\n1: { field: {} }\n ^\n"],
|
||||||
|
['notanoperation Foo { field }', 'Syntax Error: Unexpected Name "notanoperation"', "Syntax Error: Unexpected Name \"notanoperation\"\n\nGraphQL request (1:1)\n1: notanoperation Foo { field }\n ^\n"],
|
||||||
|
['...', 'Syntax Error: Unexpected ...', "Syntax Error: Unexpected ...\n\nGraphQL request (1:1)\n1: ...\n ^\n"],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider parseProvidesUsefulErrors
|
* @dataProvider parseProvidesUsefulErrors
|
||||||
* @see it('parse provides useful errors')
|
* @see it('parse provides useful errors')
|
||||||
*/
|
*/
|
||||||
public function testParseProvidesUsefulErrors($str, $expectedMessage, $stringRepresentation, $expectedPositions = null, $expectedLocations = null) : void
|
public function testParseProvidesUsefulErrors(
|
||||||
{
|
$str,
|
||||||
|
$expectedMessage,
|
||||||
|
$stringRepresentation,
|
||||||
|
$expectedPositions = null,
|
||||||
|
$expectedLocations = null
|
||||||
|
) : void {
|
||||||
try {
|
try {
|
||||||
Parser::parse($str);
|
Parser::parse($str);
|
||||||
$this->fail('Expected exception not thrown');
|
$this->fail('Expected exception not thrown');
|
||||||
@ -110,10 +133,27 @@ fragment MissingOn Type
|
|||||||
$this->expectSyntaxError(
|
$this->expectSyntaxError(
|
||||||
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
|
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
|
||||||
'Unexpected $',
|
'Unexpected $',
|
||||||
$this->loc(1,37)
|
$this->loc(1, 37)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function expectSyntaxError($text, $message, $location)
|
||||||
|
{
|
||||||
|
$this->expectException(SyntaxError::class);
|
||||||
|
$this->expectExceptionMessage($message);
|
||||||
|
try {
|
||||||
|
Parser::parse($text);
|
||||||
|
} catch (SyntaxError $error) {
|
||||||
|
$this->assertEquals([$location], $error->getLocations());
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loc($line, $column)
|
||||||
|
{
|
||||||
|
return new SourceLocation($line, $column);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('does not accept fragments spread of "on"')
|
* @see it('does not accept fragments spread of "on"')
|
||||||
*/
|
*/
|
||||||
@ -122,7 +162,7 @@ fragment MissingOn Type
|
|||||||
$this->expectSyntaxError(
|
$this->expectSyntaxError(
|
||||||
'fragment on on on { on }',
|
'fragment on on on { on }',
|
||||||
'Unexpected Name "on"',
|
'Unexpected Name "on"',
|
||||||
$this->loc(1,10)
|
$this->loc(1, 10)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +174,7 @@ fragment MissingOn Type
|
|||||||
$this->expectSyntaxError(
|
$this->expectSyntaxError(
|
||||||
'{ ...on }',
|
'{ ...on }',
|
||||||
'Expected Name, found }',
|
'Expected Name, found }',
|
||||||
$this->loc(1,9)
|
$this->loc(1, 9)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +185,7 @@ fragment MissingOn Type
|
|||||||
{
|
{
|
||||||
// Note: \u0A0A could be naively interpretted as two line-feed chars.
|
// Note: \u0A0A could be naively interpretted as two line-feed chars.
|
||||||
|
|
||||||
$char = Utils::chr(0x0A0A);
|
$char = Utils::chr(0x0A0A);
|
||||||
$query = <<<HEREDOC
|
$query = <<<HEREDOC
|
||||||
# This comment has a $char multi-byte character.
|
# This comment has a $char multi-byte character.
|
||||||
{ field(arg: "Has a $char multi-byte character.") }
|
{ field(arg: "Has a $char multi-byte character.") }
|
||||||
@ -156,18 +196,18 @@ HEREDOC;
|
|||||||
$expected = new SelectionSetNode([
|
$expected = new SelectionSetNode([
|
||||||
'selections' => new NodeList([
|
'selections' => new NodeList([
|
||||||
new FieldNode([
|
new FieldNode([
|
||||||
'name' => new NameNode(['value' => 'field']),
|
'name' => new NameNode(['value' => 'field']),
|
||||||
'arguments' => new NodeList([
|
'arguments' => new NodeList([
|
||||||
new ArgumentNode([
|
new ArgumentNode([
|
||||||
'name' => new NameNode(['value' => 'arg']),
|
'name' => new NameNode(['value' => 'arg']),
|
||||||
'value' => new StringValueNode([
|
'value' => new StringValueNode(
|
||||||
'value' => "Has a $char multi-byte character."
|
['value' => sprintf('Has a %s multi-byte character.', $char)]
|
||||||
])
|
),
|
||||||
])
|
]),
|
||||||
]),
|
]),
|
||||||
'directives' => new NodeList([])
|
'directives' => new NodeList([]),
|
||||||
])
|
]),
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->definitions[0]->selectionSet);
|
$this->assertEquals($expected, $result->definitions[0]->selectionSet);
|
||||||
@ -180,7 +220,7 @@ HEREDOC;
|
|||||||
{
|
{
|
||||||
// Following should not throw:
|
// Following should not throw:
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$result = Parser::parse($kitchenSink);
|
$result = Parser::parse($kitchenSink);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +236,7 @@ HEREDOC;
|
|||||||
'mutation',
|
'mutation',
|
||||||
'subscription',
|
'subscription',
|
||||||
'true',
|
'true',
|
||||||
'false'
|
'false',
|
||||||
];
|
];
|
||||||
foreach ($nonKeywords as $keyword) {
|
foreach ($nonKeywords as $keyword) {
|
||||||
$fragmentName = $keyword;
|
$fragmentName = $keyword;
|
||||||
@ -205,14 +245,19 @@ HEREDOC;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expected not to throw:
|
// Expected not to throw:
|
||||||
$result = Parser::parse("query $keyword {
|
$result = Parser::parse(<<<GRAPHQL
|
||||||
... $fragmentName
|
query $keyword {
|
||||||
... on $keyword { field }
|
... $fragmentName
|
||||||
|
... on $keyword { field }
|
||||||
}
|
}
|
||||||
fragment $fragmentName on Type {
|
fragment $fragmentName on Type {
|
||||||
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
|
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
|
||||||
}
|
}
|
||||||
");
|
fragment $fragmentName on Type {
|
||||||
|
$keyword($keyword: \$$keyword) @$keyword($keyword: $keyword)
|
||||||
|
}
|
||||||
|
GRAPHQL
|
||||||
|
);
|
||||||
$this->assertNotEmpty($result);
|
$this->assertNotEmpty($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,94 +331,102 @@ fragment $fragmentName on Type {
|
|||||||
');
|
');
|
||||||
$result = Parser::parse($source);
|
$result = Parser::parse($source);
|
||||||
|
|
||||||
$loc = function($start, $end) use ($source) {
|
$loc = function ($start, $end) {
|
||||||
return [
|
return [
|
||||||
'start' => $start,
|
'start' => $start,
|
||||||
'end' => $end
|
'end' => $end,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => NodeKind::DOCUMENT,
|
'kind' => NodeKind::DOCUMENT,
|
||||||
'loc' => $loc(0, 41),
|
'loc' => $loc(0, 41),
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::OPERATION_DEFINITION,
|
'kind' => NodeKind::OPERATION_DEFINITION,
|
||||||
'loc' => $loc(0, 40),
|
'loc' => $loc(0, 40),
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'variableDefinitions' => [],
|
'variableDefinitions' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(0, 40),
|
'loc' => $loc(0, 40),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(4, 38),
|
'loc' => $loc(4, 38),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(4, 8),
|
'loc' => $loc(4, 8),
|
||||||
'value' => 'node'
|
'value' => 'node',
|
||||||
],
|
],
|
||||||
'arguments' => [
|
'arguments' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::ARGUMENT,
|
'kind' => NodeKind::ARGUMENT,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(9, 11),
|
'loc' => $loc(9, 11),
|
||||||
'value' => 'id'
|
'value' => 'id',
|
||||||
],
|
],
|
||||||
'value' => [
|
'value' => [
|
||||||
'kind' => NodeKind::INT,
|
'kind' => NodeKind::INT,
|
||||||
'loc' => $loc(13, 14),
|
'loc' => $loc(13, 14),
|
||||||
'value' => '4'
|
'value' => '4',
|
||||||
],
|
],
|
||||||
'loc' => $loc(9, 14, $source)
|
'loc' => $loc(9, 14, $source),
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(16, 38),
|
'loc' => $loc(16, 38),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(22, 24),
|
'loc' => $loc(22, 24),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(22, 24),
|
'loc' => $loc(22, 24),
|
||||||
'value' => 'id'
|
'value' => 'id',
|
||||||
],
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => null
|
'selectionSet' => null,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(30, 34),
|
'loc' => $loc(30, 34),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(30, 34),
|
'loc' => $loc(30, 34),
|
||||||
'value' => 'name'
|
'value' => 'name',
|
||||||
],
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => null
|
'selectionSet' => null,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $this->nodeToArray($result));
|
$this->assertEquals($expected, self::nodeToArray($result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public static function nodeToArray(Node $node) : array
|
||||||
|
{
|
||||||
|
return TestUtils::nodeToArray($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -389,63 +442,63 @@ fragment $fragmentName on Type {
|
|||||||
');
|
');
|
||||||
$result = Parser::parse($source);
|
$result = Parser::parse($source);
|
||||||
|
|
||||||
$loc = function($start, $end) use ($source) {
|
$loc = function ($start, $end) {
|
||||||
return [
|
return [
|
||||||
'start' => $start,
|
'start' => $start,
|
||||||
'end' => $end
|
'end' => $end,
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => NodeKind::DOCUMENT,
|
'kind' => NodeKind::DOCUMENT,
|
||||||
'loc' => $loc(0, 30),
|
'loc' => $loc(0, 30),
|
||||||
'definitions' => [
|
'definitions' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::OPERATION_DEFINITION,
|
'kind' => NodeKind::OPERATION_DEFINITION,
|
||||||
'loc' => $loc(0, 29),
|
'loc' => $loc(0, 29),
|
||||||
'operation' => 'query',
|
'operation' => 'query',
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'variableDefinitions' => [],
|
'variableDefinitions' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(6, 29),
|
'loc' => $loc(6, 29),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(10, 27),
|
'loc' => $loc(10, 27),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(10, 14),
|
'loc' => $loc(10, 14),
|
||||||
'value' => 'node'
|
'value' => 'node',
|
||||||
],
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => [
|
'selectionSet' => [
|
||||||
'kind' => NodeKind::SELECTION_SET,
|
'kind' => NodeKind::SELECTION_SET,
|
||||||
'loc' => $loc(15, 27),
|
'loc' => $loc(15, 27),
|
||||||
'selections' => [
|
'selections' => [
|
||||||
[
|
[
|
||||||
'kind' => NodeKind::FIELD,
|
'kind' => NodeKind::FIELD,
|
||||||
'loc' => $loc(21, 23),
|
'loc' => $loc(21, 23),
|
||||||
'alias' => null,
|
'alias' => null,
|
||||||
'name' => [
|
'name' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAME,
|
||||||
'loc' => $loc(21, 23),
|
'loc' => $loc(21, 23),
|
||||||
'value' => 'id'
|
'value' => 'id',
|
||||||
],
|
],
|
||||||
'arguments' => [],
|
'arguments' => [],
|
||||||
'directives' => [],
|
'directives' => [],
|
||||||
'selectionSet' => null
|
'selectionSet' => null,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $this->nodeToArray($result));
|
$this->assertEquals($expected, $this->nodeToArray($result));
|
||||||
@ -475,6 +528,8 @@ fragment $fragmentName on Type {
|
|||||||
Parser::parse($source);
|
Parser::parse($source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: parseValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('contains location information that only stringifys start/end')
|
* @see it('contains location information that only stringifys start/end')
|
||||||
*/
|
*/
|
||||||
@ -495,6 +550,8 @@ fragment $fragmentName on Type {
|
|||||||
$this->assertEquals($source, $result->loc->source);
|
$this->assertEquals($source, $result->loc->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: parseType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('contains references to start and end tokens')
|
* @see it('contains references to start and end tokens')
|
||||||
*/
|
*/
|
||||||
@ -506,17 +563,18 @@ fragment $fragmentName on Type {
|
|||||||
$this->assertEquals('<EOF>', $result->loc->endToken->kind);
|
$this->assertEquals('<EOF>', $result->loc->endToken->kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: parseValue
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('parses null value')
|
* @see it('parses null value')
|
||||||
*/
|
*/
|
||||||
public function testParsesNullValues() : void
|
public function testParsesNullValues() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::NULL,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 4]
|
'kind' => NodeKind::NULL,
|
||||||
], $this->nodeToArray(Parser::parseValue('null')));
|
'loc' => ['start' => 0, 'end' => 4],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseValue('null'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -524,41 +582,45 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesListValues() : void
|
public function testParsesListValues() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::LST,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 11],
|
'kind' => NodeKind::LST,
|
||||||
'values' => [
|
'loc' => ['start' => 0, 'end' => 11],
|
||||||
[
|
'values' => [
|
||||||
'kind' => NodeKind::INT,
|
[
|
||||||
'loc' => ['start' => 1, 'end' => 4],
|
'kind' => NodeKind::INT,
|
||||||
'value' => '123'
|
'loc' => ['start' => 1, 'end' => 4],
|
||||||
|
'value' => '123',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'kind' => NodeKind::STRING,
|
||||||
|
'loc' => ['start' => 5, 'end' => 10],
|
||||||
|
'value' => 'abc',
|
||||||
|
'block' => false,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
[
|
],
|
||||||
'kind' => NodeKind::STRING,
|
$this->nodeToArray(Parser::parseValue('[123 "abc"]'))
|
||||||
'loc' => ['start' => 5, 'end' => 10],
|
);
|
||||||
'value' => 'abc',
|
|
||||||
'block' => false
|
|
||||||
]
|
|
||||||
]
|
|
||||||
], $this->nodeToArray(Parser::parseValue('[123 "abc"]')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: parseType
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('parses well known types')
|
* @see it('parses well known types')
|
||||||
*/
|
*/
|
||||||
public function testParsesWellKnownTypes() : void
|
public function testParsesWellKnownTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'String'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'value' => 'String',
|
||||||
], $this->nodeToArray(Parser::parseType('String')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('String'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -566,15 +628,18 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesCustomTypes() : void
|
public function testParsesCustomTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'value' => 'MyType',
|
||||||
], $this->nodeToArray(Parser::parseType('MyType')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('MyType'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -582,19 +647,22 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesListTypes() : void
|
public function testParsesListTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::LIST_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 8],
|
'kind' => NodeKind::LIST_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 8],
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'type' => [
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
]
|
'value' => 'MyType',
|
||||||
]
|
],
|
||||||
], $this->nodeToArray(Parser::parseType('[MyType]')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('[MyType]'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -602,19 +670,22 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesNonNullTypes() : void
|
public function testParsesNonNullTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::NON_NULL_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 7],
|
'kind' => NodeKind::NON_NULL_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 7],
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'type' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'name' => [
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
'kind' => NodeKind::NAME,
|
'name' => [
|
||||||
'loc' => ['start' => 0, 'end' => 6],
|
'kind' => NodeKind::NAME,
|
||||||
'value' => 'MyType'
|
'loc' => ['start' => 0, 'end' => 6],
|
||||||
]
|
'value' => 'MyType',
|
||||||
]
|
],
|
||||||
], $this->nodeToArray(Parser::parseType('MyType!')));
|
],
|
||||||
|
],
|
||||||
|
$this->nodeToArray(Parser::parseType('MyType!'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -622,48 +693,25 @@ fragment $fragmentName on Type {
|
|||||||
*/
|
*/
|
||||||
public function testParsesNestedTypes() : void
|
public function testParsesNestedTypes() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'kind' => NodeKind::LIST_TYPE,
|
[
|
||||||
'loc' => ['start' => 0, 'end' => 9],
|
'kind' => NodeKind::LIST_TYPE,
|
||||||
'type' => [
|
'loc' => ['start' => 0, 'end' => 9],
|
||||||
'kind' => NodeKind::NON_NULL_TYPE,
|
|
||||||
'loc' => ['start' => 1, 'end' => 8],
|
|
||||||
'type' => [
|
'type' => [
|
||||||
'kind' => NodeKind::NAMED_TYPE,
|
'kind' => NodeKind::NON_NULL_TYPE,
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'loc' => ['start' => 1, 'end' => 8],
|
||||||
'name' => [
|
'type' => [
|
||||||
'kind' => NodeKind::NAME,
|
'kind' => NodeKind::NAMED_TYPE,
|
||||||
'loc' => ['start' => 1, 'end' => 7],
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
'value' => 'MyType'
|
'name' => [
|
||||||
]
|
'kind' => NodeKind::NAME,
|
||||||
]
|
'loc' => ['start' => 1, 'end' => 7],
|
||||||
]
|
'value' => 'MyType',
|
||||||
], $this->nodeToArray(Parser::parseType('[MyType!]')));
|
],
|
||||||
}
|
],
|
||||||
|
],
|
||||||
/**
|
],
|
||||||
* @param Node $node
|
$this->nodeToArray(Parser::parseType('[MyType!]'))
|
||||||
* @return array
|
);
|
||||||
*/
|
|
||||||
public static function nodeToArray(Node $node)
|
|
||||||
{
|
|
||||||
return TestUtils::nodeToArray($node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loc($line, $column)
|
|
||||||
{
|
|
||||||
return new SourceLocation($line, $column);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function expectSyntaxError($text, $message, $location)
|
|
||||||
{
|
|
||||||
$this->expectException(SyntaxError::class);
|
|
||||||
$this->expectExceptionMessage($message);
|
|
||||||
try {
|
|
||||||
Parser::parse($text);
|
|
||||||
} catch (SyntaxError $error) {
|
|
||||||
$this->assertEquals([$location], $error->getLocations());
|
|
||||||
throw $error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Language;
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Language\AST\DocumentNode;
|
|
||||||
use GraphQL\Language\AST\EnumValueNode;
|
|
||||||
use GraphQL\Language\AST\FieldNode;
|
use GraphQL\Language\AST\FieldNode;
|
||||||
use GraphQL\Language\AST\NameNode;
|
use GraphQL\Language\AST\NameNode;
|
||||||
use GraphQL\Language\AST\OperationDefinitionNode;
|
|
||||||
use GraphQL\Language\AST\SelectionSetNode;
|
|
||||||
use GraphQL\Language\AST\StringValueNode;
|
|
||||||
use GraphQL\Language\AST\VariableNode;
|
|
||||||
use GraphQL\Language\AST\VariableDefinitionNode;
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\Printer;
|
use GraphQL\Language\Printer;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function file_get_contents;
|
||||||
|
|
||||||
class PrinterTest extends TestCase
|
class PrinterTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -22,7 +19,7 @@ class PrinterTest extends TestCase
|
|||||||
public function testDoesntAlterAST() : void
|
public function testDoesntAlterAST() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
|
|
||||||
$astCopy = $ast->cloneDeep();
|
$astCopy = $ast->cloneDeep();
|
||||||
$this->assertEquals($astCopy, $ast);
|
$this->assertEquals($astCopy, $ast);
|
||||||
@ -66,7 +63,7 @@ class PrinterTest extends TestCase
|
|||||||
$this->assertEquals($expected, Printer::doPrint($queryAstShorthanded));
|
$this->assertEquals($expected, Printer::doPrint($queryAstShorthanded));
|
||||||
|
|
||||||
$mutationAst = Parser::parse('mutation { id, name }');
|
$mutationAst = Parser::parse('mutation { id, name }');
|
||||||
$expected = 'mutation {
|
$expected = 'mutation {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -76,7 +73,7 @@ class PrinterTest extends TestCase
|
|||||||
$queryAstWithArtifacts = Parser::parse(
|
$queryAstWithArtifacts = Parser::parse(
|
||||||
'query ($foo: TestType) @testDirective { id, name }'
|
'query ($foo: TestType) @testDirective { id, name }'
|
||||||
);
|
);
|
||||||
$expected = 'query ($foo: TestType) @testDirective {
|
$expected = 'query ($foo: TestType) @testDirective {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -86,7 +83,7 @@ class PrinterTest extends TestCase
|
|||||||
$mutationAstWithArtifacts = Parser::parse(
|
$mutationAstWithArtifacts = Parser::parse(
|
||||||
'mutation ($foo: TestType) @testDirective { id, name }'
|
'mutation ($foo: TestType) @testDirective { id, name }'
|
||||||
);
|
);
|
||||||
$expected = 'mutation ($foo: TestType) @testDirective {
|
$expected = 'mutation ($foo: TestType) @testDirective {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -100,13 +97,13 @@ class PrinterTest extends TestCase
|
|||||||
public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace() : void
|
public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace() : void
|
||||||
{
|
{
|
||||||
$mutationAstWithArtifacts = Parser::parse(
|
$mutationAstWithArtifacts = Parser::parse(
|
||||||
'{ field(arg: """ space-led value""") }'
|
'{ field(arg: """ space-led value""") }'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """ space-led value""")
|
field(arg: """ space-led value""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,8 +119,8 @@ class PrinterTest extends TestCase
|
|||||||
indentation
|
indentation
|
||||||
""")
|
""")
|
||||||
}'
|
}'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """
|
field(arg: """
|
||||||
first
|
first
|
||||||
line
|
line
|
||||||
@ -131,7 +128,7 @@ class PrinterTest extends TestCase
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,7 +142,7 @@ class PrinterTest extends TestCase
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$expected = <<<END
|
$expected = <<<END
|
||||||
{
|
{
|
||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
@ -160,7 +157,8 @@ END;
|
|||||||
*/
|
*/
|
||||||
public function testExperimentalCorrectlyPrintsFragmentDefinedVariables() : void
|
public function testExperimentalCorrectlyPrintsFragmentDefinedVariables() : void
|
||||||
{
|
{
|
||||||
$fragmentWithVariable = Parser::parse('
|
$fragmentWithVariable = Parser::parse(
|
||||||
|
'
|
||||||
fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
|
fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
@ -187,13 +185,13 @@ END;
|
|||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
}'
|
}'
|
||||||
);
|
);
|
||||||
$expected = '{
|
$expected = '{
|
||||||
field(arg: """ space-led value "quoted string"
|
field(arg: """ space-led value "quoted string"
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +200,7 @@ END;
|
|||||||
public function testPrintsKitchenSink() : void
|
public function testPrintsKitchenSink() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
|
|
||||||
$printed = Printer::doPrint($ast);
|
$printed = Printer::doPrint($ast);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests;
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Language\AST\NameNode;
|
use GraphQL\Language\AST\NameNode;
|
||||||
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
||||||
@ -7,6 +10,7 @@ use GraphQL\Language\Parser;
|
|||||||
use GraphQL\Language\Printer;
|
use GraphQL\Language\Printer;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
use function file_get_contents;
|
||||||
|
|
||||||
class SchemaPrinterTest extends TestCase
|
class SchemaPrinterTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -16,7 +20,7 @@ class SchemaPrinterTest extends TestCase
|
|||||||
public function testPrintsMinimalAst() : void
|
public function testPrintsMinimalAst() : void
|
||||||
{
|
{
|
||||||
$ast = new ScalarTypeDefinitionNode([
|
$ast = new ScalarTypeDefinitionNode([
|
||||||
'name' => new NameNode(['value' => 'foo'])
|
'name' => new NameNode(['value' => 'foo']),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('scalar foo', Printer::doPrint($ast));
|
$this->assertEquals('scalar foo', Printer::doPrint($ast));
|
||||||
}
|
}
|
||||||
@ -41,7 +45,7 @@ class SchemaPrinterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
||||||
|
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$astCopy = $ast->cloneDeep();
|
$astCopy = $ast->cloneDeep();
|
||||||
Printer::doPrint($ast);
|
Printer::doPrint($ast);
|
||||||
|
|
||||||
@ -52,7 +56,7 @@ class SchemaPrinterTest extends TestCase
|
|||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql');
|
||||||
|
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$printed = Printer::doPrint($ast);
|
$printed = Printer::doPrint($ast);
|
||||||
|
|
||||||
$expected = 'schema {
|
$expected = 'schema {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests;
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Language\AST\Location;
|
use GraphQL\Language\AST\Location;
|
||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
@ -7,80 +10,68 @@ use GraphQL\Language\AST\NodeList;
|
|||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Utils\AST;
|
use GraphQL\Utils\AST;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function array_keys;
|
||||||
|
use function count;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function get_class;
|
||||||
|
use function get_object_vars;
|
||||||
|
use function implode;
|
||||||
|
use function json_decode;
|
||||||
|
|
||||||
class SerializationTest extends TestCase
|
class SerializationTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testSerializesAst() : void
|
public function testSerializesAst() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$ast = Parser::parse($kitchenSink);
|
$ast = Parser::parse($kitchenSink);
|
||||||
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
||||||
$this->assertEquals($expectedAst, $ast->toArray(true));
|
$this->assertEquals($expectedAst, $ast->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnserializesAst() : void
|
public function testUnserializesAst() : void
|
||||||
{
|
{
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink.ast'), true);
|
||||||
$actualAst = AST::fromArray($serializedAst);
|
$actualAst = AST::fromArray($serializedAst);
|
||||||
$parsedAst = Parser::parse($kitchenSink);
|
$parsedAst = Parser::parse($kitchenSink);
|
||||||
$this->assertNodesAreEqual($parsedAst, $actualAst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSerializeSupportsNoLocationOption() : void
|
|
||||||
{
|
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
|
||||||
$ast = Parser::parse($kitchenSink, ['noLocation' => true]);
|
|
||||||
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
|
|
||||||
$this->assertEquals($expectedAst, $ast->toArray(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUnserializeSupportsNoLocationOption() : void
|
|
||||||
{
|
|
||||||
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
|
||||||
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
|
|
||||||
$actualAst = AST::fromArray($serializedAst);
|
|
||||||
$parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]);
|
|
||||||
$this->assertNodesAreEqual($parsedAst, $actualAst);
|
$this->assertNodesAreEqual($parsedAst, $actualAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc
|
* Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc
|
||||||
*
|
*
|
||||||
* @param $expected
|
* @param string[] $path
|
||||||
* @param $actual
|
|
||||||
* @param array $path
|
|
||||||
*/
|
*/
|
||||||
private function assertNodesAreEqual($expected, $actual, $path = [])
|
private function assertNodesAreEqual(Node $expected, Node $actual, array $path = []) : void
|
||||||
{
|
{
|
||||||
$err = "Mismatch at AST path: " . implode(', ', $path);
|
$err = 'Mismatch at AST path: ' . implode(', ', $path);
|
||||||
|
|
||||||
$this->assertInstanceOf(Node::class, $actual, $err);
|
$this->assertInstanceOf(Node::class, $actual, $err);
|
||||||
$this->assertEquals(get_class($expected), get_class($actual), $err);
|
$this->assertEquals(get_class($expected), get_class($actual), $err);
|
||||||
|
|
||||||
$expectedVars = get_object_vars($expected);
|
$expectedVars = get_object_vars($expected);
|
||||||
$actualVars = get_object_vars($actual);
|
$actualVars = get_object_vars($actual);
|
||||||
$this->assertSame(count($expectedVars), count($actualVars), $err);
|
$this->assertCount(count($expectedVars), $actualVars, $err);
|
||||||
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
|
$this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err);
|
||||||
|
|
||||||
foreach ($expectedVars as $name => $expectedValue) {
|
foreach ($expectedVars as $name => $expectedValue) {
|
||||||
$actualValue = $actualVars[$name];
|
$actualValue = $actualVars[$name];
|
||||||
$tmpPath = $path;
|
$tmpPath = $path;
|
||||||
$tmpPath[] = $name;
|
$tmpPath[] = $name;
|
||||||
$err = "Mismatch at AST path: " . implode(', ', $tmpPath);
|
$err = 'Mismatch at AST path: ' . implode(', ', $tmpPath);
|
||||||
|
|
||||||
if ($expectedValue instanceof Node) {
|
if ($expectedValue instanceof Node) {
|
||||||
$this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath);
|
$this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath);
|
||||||
} else if ($expectedValue instanceof NodeList) {
|
} elseif ($expectedValue instanceof NodeList) {
|
||||||
$this->assertEquals(count($expectedValue), count($actualValue), $err);
|
$this->assertEquals(count($expectedValue), count($actualValue), $err);
|
||||||
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
|
$this->assertInstanceOf(NodeList::class, $actualValue, $err);
|
||||||
|
|
||||||
foreach ($expectedValue as $index => $listNode) {
|
foreach ($expectedValue as $index => $listNode) {
|
||||||
$tmpPath2 = $tmpPath;
|
$tmpPath2 = $tmpPath;
|
||||||
$tmpPath2 [] = $index;
|
$tmpPath2[] = $index;
|
||||||
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
|
$this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2);
|
||||||
}
|
}
|
||||||
} else if ($expectedValue instanceof Location) {
|
} elseif ($expectedValue instanceof Location) {
|
||||||
$this->assertInstanceOf(Location::class, $actualValue, $err);
|
$this->assertInstanceOf(Location::class, $actualValue, $err);
|
||||||
$this->assertSame($expectedValue->start, $actualValue->start, $err);
|
$this->assertSame($expectedValue->start, $actualValue->start, $err);
|
||||||
$this->assertSame($expectedValue->end, $actualValue->end, $err);
|
$this->assertSame($expectedValue->end, $actualValue->end, $err);
|
||||||
@ -89,4 +80,21 @@ class SerializationTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSerializeSupportsNoLocationOption() : void
|
||||||
|
{
|
||||||
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
|
$ast = Parser::parse($kitchenSink, ['noLocation' => true]);
|
||||||
|
$expectedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
|
||||||
|
$this->assertEquals($expectedAst, $ast->toArray(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnserializeSupportsNoLocationOption() : void
|
||||||
|
{
|
||||||
|
$kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql');
|
||||||
|
$serializedAst = json_decode(file_get_contents(__DIR__ . '/kitchen-sink-noloc.ast'), true);
|
||||||
|
$actualAst = AST::fromArray($serializedAst);
|
||||||
|
$parsedAst = Parser::parse($kitchenSink, ['noLocation' => true]);
|
||||||
|
$this->assertNodesAreEqual($parsedAst, $actualAst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,41 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Language;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Language\AST\Location;
|
use GraphQL\Language\AST\Location;
|
||||||
use GraphQL\Language\AST\Node;
|
use GraphQL\Language\AST\Node;
|
||||||
use GraphQL\Language\AST\NodeList;
|
use GraphQL\Language\AST\NodeList;
|
||||||
|
use function get_object_vars;
|
||||||
|
use function is_array;
|
||||||
|
use function is_scalar;
|
||||||
|
|
||||||
class TestUtils
|
class TestUtils
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Node $node
|
* @return mixed[]
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function nodeToArray(Node $node)
|
public static function nodeToArray(Node $node) : array
|
||||||
{
|
{
|
||||||
$result = [
|
$result = [
|
||||||
'kind' => $node->kind,
|
'kind' => $node->kind,
|
||||||
'loc' => self::locationToArray($node->loc)
|
'loc' => self::locationToArray($node->loc),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (get_object_vars($node) as $prop => $propValue) {
|
foreach (get_object_vars($node) as $prop => $propValue) {
|
||||||
if (isset($result[$prop]))
|
if (isset($result[$prop])) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_array($propValue) || $propValue instanceof NodeList) {
|
if (is_array($propValue) || $propValue instanceof NodeList) {
|
||||||
$tmp = [];
|
$tmp = [];
|
||||||
foreach ($propValue as $tmp1) {
|
foreach ($propValue as $tmp1) {
|
||||||
$tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1;
|
$tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1;
|
||||||
}
|
}
|
||||||
} else if ($propValue instanceof Node) {
|
} elseif ($propValue instanceof Node) {
|
||||||
$tmp = self::nodeToArray($propValue);
|
$tmp = self::nodeToArray($propValue);
|
||||||
} else if (is_scalar($propValue) || null === $propValue) {
|
} elseif (is_scalar($propValue) || $propValue === null) {
|
||||||
$tmp = $propValue;
|
$tmp = $propValue;
|
||||||
} else {
|
} else {
|
||||||
$tmp = null;
|
$tmp = null;
|
||||||
@ -38,27 +43,25 @@ class TestUtils
|
|||||||
|
|
||||||
$result[$prop] = $tmp;
|
$result[$prop] = $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Location $loc
|
* @return int[]
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function locationToArray(Location $loc)
|
public static function locationToArray(Location $loc) : array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'start' => $loc->start,
|
'start' => $loc->start,
|
||||||
'end' => $loc->end
|
'end' => $loc->end,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $start
|
* @return int[]
|
||||||
* @param $end
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public static function locArray($start, $end)
|
public static function locArray(int $start, int $end) : array
|
||||||
{
|
{
|
||||||
return ['start' => $start, 'end' => $end];
|
return ['start' => $start, 'end' => $end];
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests;
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Language;
|
||||||
|
|
||||||
use GraphQL\Language\Token;
|
use GraphQL\Language\Token;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -8,12 +11,12 @@ class TokenTest extends TestCase
|
|||||||
{
|
{
|
||||||
public function testReturnTokenOnArray() : void
|
public function testReturnTokenOnArray() : void
|
||||||
{
|
{
|
||||||
$token = new Token('Kind', 1, 10, 3, 5);
|
$token = new Token('Kind', 1, 10, 3, 5);
|
||||||
$expected = [
|
$expected = [
|
||||||
'kind' => 'Kind',
|
'kind' => 'Kind',
|
||||||
'value' => null,
|
'value' => null,
|
||||||
'line' => 3,
|
'line' => 3,
|
||||||
'column' => 5
|
'column' => 5,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $token->toArray());
|
$this->assertEquals($expected, $token->toArray());
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server\Psr7;
|
namespace GraphQL\Tests\Server\Psr7;
|
||||||
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
use Psr\Http\Message\UriInterface;
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use function strtolower;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* phpcs:ignoreFile -- this is not a core file
|
||||||
|
*/
|
||||||
class PsrRequestStub implements ServerRequestInterface
|
class PsrRequestStub implements ServerRequestInterface
|
||||||
{
|
{
|
||||||
public $queryParams = [];
|
public $queryParams = [];
|
||||||
@ -13,14 +20,10 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
|
|
||||||
public $method;
|
public $method;
|
||||||
|
|
||||||
/**
|
/** @var PsrStreamStub */
|
||||||
* @var PsrStreamStub
|
|
||||||
*/
|
|
||||||
public $body;
|
public $body;
|
||||||
|
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $parsedBody;
|
public $parsedBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,7 +35,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getProtocolVersion()
|
public function getProtocolVersion()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +53,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withProtocolVersion($version)
|
public function withProtocolVersion($version)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +83,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeaders()
|
public function getHeaders()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +96,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function hasHeader($name)
|
public function hasHeader($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,7 +116,8 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
public function getHeader($name)
|
public function getHeader($name)
|
||||||
{
|
{
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
return isset($this->headers[$name]) ? $this->headers[$name] : [];
|
|
||||||
|
return $this->headers[$name] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,7 +141,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeaderLine($name)
|
public function getHeaderLine($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,14 +154,14 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
* immutability of the message, and MUST return an instance that has the
|
* immutability of the message, and MUST return an instance that has the
|
||||||
* new and/or updated header and value.
|
* new and/or updated header and value.
|
||||||
*
|
*
|
||||||
* @param string $name Case-insensitive header field name.
|
* @param string $name Case-insensitive header field name.
|
||||||
* @param string|string[] $value Header value(s).
|
* @param string|string[] $value Header value(s).
|
||||||
* @return static
|
* @return static
|
||||||
* @throws \InvalidArgumentException for invalid header names or values.
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
*/
|
*/
|
||||||
public function withHeader($name, $value)
|
public function withHeader($name, $value)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,14 +175,14 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
* immutability of the message, and MUST return an instance that has the
|
* immutability of the message, and MUST return an instance that has the
|
||||||
* new header and/or value.
|
* new header and/or value.
|
||||||
*
|
*
|
||||||
* @param string $name Case-insensitive header field name to add.
|
* @param string $name Case-insensitive header field name to add.
|
||||||
* @param string|string[] $value Header value(s).
|
* @param string|string[] $value Header value(s).
|
||||||
* @return static
|
* @return static
|
||||||
* @throws \InvalidArgumentException for invalid header names or values.
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
*/
|
*/
|
||||||
public function withAddedHeader($name, $value)
|
public function withAddedHeader($name, $value)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +199,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withoutHeader($name)
|
public function withoutHeader($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,7 +227,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withBody(StreamInterface $body)
|
public function withBody(StreamInterface $body)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,7 +248,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getRequestTarget()
|
public function getRequestTarget()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,7 +270,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withRequestTarget($requestTarget)
|
public function withRequestTarget($requestTarget)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,7 +300,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withMethod($method)
|
public function withMethod($method)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -339,13 +343,13 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
* new UriInterface instance.
|
* new UriInterface instance.
|
||||||
*
|
*
|
||||||
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
||||||
* @param UriInterface $uri New request URI to use.
|
* @param UriInterface $uri New request URI to use.
|
||||||
* @param bool $preserveHost Preserve the original state of the Host header.
|
* @param bool $preserveHost Preserve the original state of the Host header.
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public function withUri(UriInterface $uri, $preserveHost = false)
|
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -359,7 +363,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getServerParams()
|
public function getServerParams()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -374,7 +378,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getCookieParams()
|
public function getCookieParams()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,7 +400,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withCookieParams(array $cookies)
|
public function withCookieParams(array $cookies)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,7 +444,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withQueryParams(array $query)
|
public function withQueryParams(array $query)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,7 +461,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getUploadedFiles()
|
public function getUploadedFiles()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -473,7 +477,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withUploadedFiles(array $uploadedFiles)
|
public function withUploadedFiles(array $uploadedFiles)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -526,7 +530,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withParsedBody($data)
|
public function withParsedBody($data)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -542,7 +546,7 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function getAttributes()
|
public function getAttributes()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -556,13 +560,13 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
* specifying a default value to return if the attribute is not found.
|
* specifying a default value to return if the attribute is not found.
|
||||||
*
|
*
|
||||||
* @see getAttributes()
|
* @see getAttributes()
|
||||||
* @param string $name The attribute name.
|
* @param string $name The attribute name.
|
||||||
* @param mixed $default Default value to return if the attribute does not exist.
|
* @param mixed $default Default value to return if the attribute does not exist.
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getAttribute($name, $default = null)
|
public function getAttribute($name, $default = null)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -576,13 +580,13 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
* updated attribute.
|
* updated attribute.
|
||||||
*
|
*
|
||||||
* @see getAttributes()
|
* @see getAttributes()
|
||||||
* @param string $name The attribute name.
|
* @param string $name The attribute name.
|
||||||
* @param mixed $value The value of the attribute.
|
* @param mixed $value The value of the attribute.
|
||||||
* @return static
|
* @return static
|
||||||
*/
|
*/
|
||||||
public function withAttribute($name, $value)
|
public function withAttribute($name, $value)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -601,6 +605,6 @@ class PsrRequestStub implements ServerRequestInterface
|
|||||||
*/
|
*/
|
||||||
public function withoutAttribute($name)
|
public function withoutAttribute($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Server\Psr7;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Server\Psr7;
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* phpcs:ignoreFile -- this is not a core file
|
||||||
|
*/
|
||||||
class PsrResponseStub implements ResponseInterface
|
class PsrResponseStub implements ResponseInterface
|
||||||
{
|
{
|
||||||
public $headers = [];
|
public $headers = [];
|
||||||
@ -22,7 +27,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getProtocolVersion()
|
public function getProtocolVersion()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +45,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function withProtocolVersion($version)
|
public function withProtocolVersion($version)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,7 +75,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeaders()
|
public function getHeaders()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +88,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function hasHeader($name)
|
public function hasHeader($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,7 +107,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeader($name)
|
public function getHeader($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,7 +131,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getHeaderLine($name)
|
public function getHeaderLine($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,15 +144,16 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
* immutability of the message, and MUST return an instance that has the
|
* immutability of the message, and MUST return an instance that has the
|
||||||
* new and/or updated header and value.
|
* new and/or updated header and value.
|
||||||
*
|
*
|
||||||
* @param string $name Case-insensitive header field name.
|
* @param string $name Case-insensitive header field name.
|
||||||
* @param string|string[] $value Header value(s).
|
* @param string|string[] $value Header value(s).
|
||||||
* @return static
|
* @return static
|
||||||
* @throws \InvalidArgumentException for invalid header names or values.
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
*/
|
*/
|
||||||
public function withHeader($name, $value)
|
public function withHeader($name, $value)
|
||||||
{
|
{
|
||||||
$tmp = clone $this;
|
$tmp = clone $this;
|
||||||
$tmp->headers[$name][] = $value;
|
$tmp->headers[$name][] = $value;
|
||||||
|
|
||||||
return $tmp;
|
return $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,14 +168,14 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
* immutability of the message, and MUST return an instance that has the
|
* immutability of the message, and MUST return an instance that has the
|
||||||
* new header and/or value.
|
* new header and/or value.
|
||||||
*
|
*
|
||||||
* @param string $name Case-insensitive header field name to add.
|
* @param string $name Case-insensitive header field name to add.
|
||||||
* @param string|string[] $value Header value(s).
|
* @param string|string[] $value Header value(s).
|
||||||
* @return static
|
* @return static
|
||||||
* @throws \InvalidArgumentException for invalid header names or values.
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
*/
|
*/
|
||||||
public function withAddedHeader($name, $value)
|
public function withAddedHeader($name, $value)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,7 +192,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function withoutHeader($name)
|
public function withoutHeader($name)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,7 +202,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getBody()
|
public function getBody()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,8 +220,9 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function withBody(StreamInterface $body)
|
public function withBody(StreamInterface $body)
|
||||||
{
|
{
|
||||||
$tmp = clone $this;
|
$tmp = clone $this;
|
||||||
$tmp->body = $body;
|
$tmp->body = $body;
|
||||||
|
|
||||||
return $tmp;
|
return $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +236,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getStatusCode()
|
public function getStatusCode()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +252,7 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*
|
*
|
||||||
* @link http://tools.ietf.org/html/rfc7231#section-6
|
* @link http://tools.ietf.org/html/rfc7231#section-6
|
||||||
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||||
* @param int $code The 3-digit integer result code to set.
|
* @param int $code The 3-digit integer result code to set.
|
||||||
* @param string $reasonPhrase The reason phrase to use with the
|
* @param string $reasonPhrase The reason phrase to use with the
|
||||||
* provided status code; if none is provided, implementations MAY
|
* provided status code; if none is provided, implementations MAY
|
||||||
* use the defaults as suggested in the HTTP specification.
|
* use the defaults as suggested in the HTTP specification.
|
||||||
@ -254,8 +261,9 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function withStatus($code, $reasonPhrase = '')
|
public function withStatus($code, $reasonPhrase = '')
|
||||||
{
|
{
|
||||||
$tmp = clone $this;
|
$tmp = clone $this;
|
||||||
$tmp->statusCode = $code;
|
$tmp->statusCode = $code;
|
||||||
|
|
||||||
return $tmp;
|
return $tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +282,6 @@ class PsrResponseStub implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
public function getReasonPhrase()
|
public function getReasonPhrase()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server\Psr7;
|
namespace GraphQL\Tests\Server\Psr7;
|
||||||
|
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use function strlen;
|
||||||
|
use const SEEK_SET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* phpcs:ignoreFile -- this is not a core file
|
||||||
|
*/
|
||||||
class PsrStreamStub implements StreamInterface
|
class PsrStreamStub implements StreamInterface
|
||||||
{
|
{
|
||||||
public $content;
|
public $content;
|
||||||
@ -33,7 +41,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function close()
|
public function close()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +53,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function detach()
|
public function detach()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,7 +63,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function getSize()
|
public function getSize()
|
||||||
{
|
{
|
||||||
return strlen($this->content?:'');
|
return strlen($this->content ?: '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +74,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function tell()
|
public function tell()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +84,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function eof()
|
public function eof()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +94,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function isSeekable()
|
public function isSeekable()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +111,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function seek($offset, $whence = SEEK_SET)
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +126,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function rewind()
|
public function rewind()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,6 +149,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
public function write($string)
|
public function write($string)
|
||||||
{
|
{
|
||||||
$this->content = $string;
|
$this->content = $string;
|
||||||
|
|
||||||
return strlen($string);
|
return strlen($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +160,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function isReadable()
|
public function isReadable()
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,7 +175,7 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function read($length)
|
public function read($length)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,6 +204,6 @@ class PsrStreamStub implements StreamInterface
|
|||||||
*/
|
*/
|
||||||
public function getMetadata($key = null)
|
public function getMetadata($key = null)
|
||||||
{
|
{
|
||||||
throw new \Exception("Not implemented");
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Server\Helper;
|
use GraphQL\Server\Helper;
|
||||||
use GraphQL\Tests\Server\Psr7\PsrStreamStub;
|
|
||||||
use GraphQL\Tests\Server\Psr7\PsrResponseStub;
|
use GraphQL\Tests\Server\Psr7\PsrResponseStub;
|
||||||
|
use GraphQL\Tests\Server\Psr7\PsrStreamStub;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function json_encode;
|
||||||
|
|
||||||
class PsrResponseTest extends TestCase
|
class PsrResponseTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testConvertsResultToPsrResponse() : void
|
public function testConvertsResultToPsrResponse() : void
|
||||||
{
|
{
|
||||||
$result = new ExecutionResult(['key' => 'value']);
|
$result = new ExecutionResult(['key' => 'value']);
|
||||||
$stream = new PsrStreamStub();
|
$stream = new PsrStreamStub();
|
||||||
$psrResponse = new PsrResponseStub();
|
$psrResponse = new PsrResponseStub();
|
||||||
|
|
||||||
$helper = new Helper();
|
$helper = new Helper();
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Error\Debug;
|
use GraphQL\Error\Debug;
|
||||||
@ -14,17 +17,17 @@ use GraphQL\Server\ServerConfig;
|
|||||||
use GraphQL\Validator\DocumentValidator;
|
use GraphQL\Validator\DocumentValidator;
|
||||||
use GraphQL\Validator\Rules\CustomValidationRule;
|
use GraphQL\Validator\Rules\CustomValidationRule;
|
||||||
use GraphQL\Validator\ValidationContext;
|
use GraphQL\Validator\ValidationContext;
|
||||||
|
use function count;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class QueryExecutionTest extends ServerTestCase
|
class QueryExecutionTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
/**
|
/** @var ServerConfig */
|
||||||
* @var ServerConfig
|
|
||||||
*/
|
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$schema = $this->buildSchema();
|
$schema = $this->buildSchema();
|
||||||
$this->config = ServerConfig::create()
|
$this->config = ServerConfig::create()
|
||||||
->setSchema($schema);
|
->setSchema($schema);
|
||||||
}
|
}
|
||||||
@ -34,20 +37,36 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$query = '{f1}';
|
$query = '{f1}';
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['f1' => 'f1'],
|
||||||
'f1' => 'f1'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertQueryResultEquals($expected, $query);
|
$this->assertQueryResultEquals($expected, $query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertQueryResultEquals($expected, $query, $variables = null)
|
||||||
|
{
|
||||||
|
$result = $this->executeQuery($query, $variables);
|
||||||
|
$this->assertArraySubset($expected, $result->toArray(true));
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeQuery($query, $variables = null, $readonly = false)
|
||||||
|
{
|
||||||
|
$op = OperationParams::create(['query' => $query, 'variables' => $variables], $readonly);
|
||||||
|
$helper = new Helper();
|
||||||
|
$result = $helper->executeOperation($this->config, $op);
|
||||||
|
$this->assertInstanceOf(ExecutionResult::class, $result);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function testReturnsSyntaxErrors() : void
|
public function testReturnsSyntaxErrors() : void
|
||||||
{
|
{
|
||||||
$query = '{f1';
|
$query = '{f1';
|
||||||
|
|
||||||
$result = $this->executeQuery($query);
|
$result = $this->executeQuery($query);
|
||||||
$this->assertSame(null, $result->data);
|
$this->assertNull($result->data);
|
||||||
$this->assertCount(1, $result->errors);
|
$this->assertCount(1, $result->errors);
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'Syntax Error: Expected Name, found <EOF>',
|
'Syntax Error: Expected Name, found <EOF>',
|
||||||
@ -70,11 +89,12 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'fieldWithSafeException' => null,
|
'fieldWithSafeException' => null,
|
||||||
'f1' => 'f1'
|
'f1' => 'f1',
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'This is the exception we want',
|
'message' => 'This is the exception we want',
|
||||||
|
|
||||||
'path' => ['fieldWithSafeException'],
|
'path' => ['fieldWithSafeException'],
|
||||||
'trace' => []
|
'trace' => []
|
||||||
]
|
]
|
||||||
@ -100,7 +120,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testPassesRootValueAndContext() : void
|
public function testPassesRootValueAndContext() : void
|
||||||
{
|
{
|
||||||
$rootValue = 'myRootValue';
|
$rootValue = 'myRootValue';
|
||||||
$context = new \stdClass();
|
$context = new \stdClass();
|
||||||
|
|
||||||
$this->config
|
$this->config
|
||||||
->setContext($context)
|
->setContext($context)
|
||||||
@ -112,7 +132,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
|
|
||||||
$this->assertTrue(!isset($context->testedRootValue));
|
$this->assertTrue(! isset($context->testedRootValue));
|
||||||
$this->executeQuery($query);
|
$this->executeQuery($query);
|
||||||
$this->assertSame($rootValue, $context->testedRootValue);
|
$this->assertSame($rootValue, $context->testedRootValue);
|
||||||
}
|
}
|
||||||
@ -120,30 +140,30 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testPassesVariables() : void
|
public function testPassesVariables() : void
|
||||||
{
|
{
|
||||||
$variables = ['a' => 'a', 'b' => 'b'];
|
$variables = ['a' => 'a', 'b' => 'b'];
|
||||||
$query = '
|
$query = '
|
||||||
query ($a: String!, $b: String!) {
|
query ($a: String!, $b: String!) {
|
||||||
a: fieldWithArg(arg: $a)
|
a: fieldWithArg(arg: $a)
|
||||||
b: fieldWithArg(arg: $b)
|
b: fieldWithArg(arg: $b)
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'a' => 'a',
|
'a' => 'a',
|
||||||
'b' => 'b'
|
'b' => 'b',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertQueryResultEquals($expected, $query, $variables);
|
$this->assertQueryResultEquals($expected, $query, $variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPassesCustomValidationRules() : void
|
public function testPassesCustomValidationRules() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
{nonExistentField}
|
{nonExistentField}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['message' => 'Cannot query field "nonExistentField" on type "Query".']
|
['message' => 'Cannot query field "nonExistentField" on type "Query".'],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertQueryResultEquals($expected, $query);
|
$this->assertQueryResultEquals($expected, $query);
|
||||||
@ -151,15 +171,16 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$called = false;
|
$called = false;
|
||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
new CustomValidationRule('SomeRule', function() use (&$called) {
|
new CustomValidationRule('SomeRule', function () use (&$called) {
|
||||||
$called = true;
|
$called = true;
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->config->setValidationRules($rules);
|
$this->config->setValidationRules($rules);
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => []
|
'data' => [],
|
||||||
];
|
];
|
||||||
$this->assertQueryResultEquals($expected, $query);
|
$this->assertQueryResultEquals($expected, $query);
|
||||||
$this->assertTrue($called);
|
$this->assertTrue($called);
|
||||||
@ -170,11 +191,12 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$called = false;
|
$called = false;
|
||||||
$params = $doc = $operationType = null;
|
$params = $doc = $operationType = null;
|
||||||
|
|
||||||
$this->config->setValidationRules(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
$this->config->setValidationRules(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$params = $p;
|
$params = $p;
|
||||||
$doc = $d;
|
$doc = $d;
|
||||||
$operationType = $o;
|
$operationType = $o;
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -188,23 +210,25 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
public function testAllowsDifferentValidationRulesDependingOnOperation() : void
|
public function testAllowsDifferentValidationRulesDependingOnOperation() : void
|
||||||
{
|
{
|
||||||
$q1 = '{f1}';
|
$q1 = '{f1}';
|
||||||
$q2 = '{invalid}';
|
$q2 = '{invalid}';
|
||||||
$called1 = false;
|
$called1 = false;
|
||||||
$called2 = false;
|
$called2 = false;
|
||||||
|
|
||||||
$this->config->setValidationRules(function(OperationParams $params) use ($q1, $q2, &$called1, &$called2) {
|
$this->config->setValidationRules(function (OperationParams $params) use ($q1, &$called1, &$called2) {
|
||||||
if ($params->query === $q1) {
|
if ($params->query === $q1) {
|
||||||
$called1 = true;
|
$called1 = true;
|
||||||
|
|
||||||
return DocumentValidator::allRules();
|
return DocumentValidator::allRules();
|
||||||
} else {
|
|
||||||
$called2 = true;
|
|
||||||
return [
|
|
||||||
new CustomValidationRule('MyRule', function(ValidationContext $context) {
|
|
||||||
$context->reportError(new Error("This is the error we are looking for!"));
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$called2 = true;
|
||||||
|
|
||||||
|
return [
|
||||||
|
new CustomValidationRule('MyRule', function (ValidationContext $context) {
|
||||||
|
$context->reportError(new Error('This is the error we are looking for!'));
|
||||||
|
}),
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
$expected = ['data' => ['f1' => 'f1']];
|
$expected = ['data' => ['f1' => 'f1']];
|
||||||
@ -212,8 +236,8 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$this->assertTrue($called1);
|
$this->assertTrue($called1);
|
||||||
$this->assertFalse($called2);
|
$this->assertFalse($called2);
|
||||||
|
|
||||||
$called1 = false;
|
$called1 = false;
|
||||||
$called2 = false;
|
$called2 = false;
|
||||||
$expected = ['errors' => [['message' => 'This is the error we are looking for!']]];
|
$expected = ['errors' => [['message' => 'This is the error we are looking for!']]];
|
||||||
$this->assertQueryResultEquals($expected, $q2);
|
$this->assertQueryResultEquals($expected, $q2);
|
||||||
$this->assertFalse($called1);
|
$this->assertFalse($called1);
|
||||||
@ -223,7 +247,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testAllowsSkippingValidation() : void
|
public function testAllowsSkippingValidation() : void
|
||||||
{
|
{
|
||||||
$this->config->setValidationRules([]);
|
$this->config->setValidationRules([]);
|
||||||
$query = '{nonExistentField}';
|
$query = '{nonExistentField}';
|
||||||
$expected = ['data' => []];
|
$expected = ['data' => []];
|
||||||
$this->assertQueryResultEquals($expected, $query);
|
$this->assertQueryResultEquals($expected, $query);
|
||||||
}
|
}
|
||||||
@ -235,23 +259,29 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Persisted queries are not supported by this server',
|
'message' => 'Persisted queries are not supported by this server',
|
||||||
'category' => 'request'
|
'category' => 'request',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function executePersistedQuery($queryId, $variables = null)
|
||||||
|
{
|
||||||
|
$op = OperationParams::create(['queryId' => $queryId, 'variables' => $variables]);
|
||||||
|
$helper = new Helper();
|
||||||
|
$result = $helper->executeOperation($this->config, $op);
|
||||||
|
$this->assertInstanceOf(ExecutionResult::class, $result);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function testBatchedQueriesAreDisabledByDefault() : void
|
public function testBatchedQueriesAreDisabledByDefault() : void
|
||||||
{
|
{
|
||||||
$batch = [
|
$batch = [
|
||||||
[
|
['query' => '{invalid}'],
|
||||||
'query' => '{invalid}'
|
['query' => '{f1,fieldWithSafeException}'],
|
||||||
],
|
|
||||||
[
|
|
||||||
'query' => '{f1,fieldWithSafeException}'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = $this->executeBatchedQuery($batch);
|
$result = $this->executeBatchedQuery($batch);
|
||||||
@ -260,18 +290,18 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
[
|
[
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Batched queries are not supported by this server',
|
'message' => 'Batched queries are not supported by this server',
|
||||||
'category' => 'request'
|
'category' => 'request',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Batched queries are not supported by this server',
|
'message' => 'Batched queries are not supported by this server',
|
||||||
'category' => 'request'
|
'category' => 'request',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -279,6 +309,31 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$this->assertEquals($expected[1], $result[1]->toArray());
|
$this->assertEquals($expected[1], $result[1]->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[][] $qs
|
||||||
|
*/
|
||||||
|
private function executeBatchedQuery(array $qs)
|
||||||
|
{
|
||||||
|
$batch = [];
|
||||||
|
foreach ($qs as $params) {
|
||||||
|
$batch[] = OperationParams::create($params);
|
||||||
|
}
|
||||||
|
$helper = new Helper();
|
||||||
|
$result = $helper->executeBatch($this->config, $batch);
|
||||||
|
$this->assertInternalType('array', $result);
|
||||||
|
$this->assertCount(count($qs), $result);
|
||||||
|
|
||||||
|
foreach ($result as $index => $entry) {
|
||||||
|
$this->assertInstanceOf(
|
||||||
|
ExecutionResult::class,
|
||||||
|
$entry,
|
||||||
|
sprintf('Result at %s is not an instance of %s', $index, ExecutionResult::class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function testMutationsAreNotAllowedInReadonlyMode() : void
|
public function testMutationsAreNotAllowedInReadonlyMode() : void
|
||||||
{
|
{
|
||||||
$mutation = 'mutation { a }';
|
$mutation = 'mutation { a }';
|
||||||
@ -286,10 +341,10 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'GET supports only query operation',
|
'message' => 'GET supports only query operation',
|
||||||
'category' => 'request'
|
'category' => 'request',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = $this->executeQuery($mutation, null, true);
|
$result = $this->executeQuery($mutation, null, true);
|
||||||
@ -299,9 +354,10 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testAllowsPersistentQueries() : void
|
public function testAllowsPersistentQueries() : void
|
||||||
{
|
{
|
||||||
$called = false;
|
$called = false;
|
||||||
$this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) {
|
$this->config->setPersistentQueryLoader(function ($queryId, OperationParams $params) use (&$called) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$this->assertEquals('some-id', $queryId);
|
$this->assertEquals('some-id', $queryId);
|
||||||
|
|
||||||
return '{f1}';
|
return '{f1}';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -309,17 +365,16 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$this->assertTrue($called);
|
$this->assertTrue($called);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['f1' => 'f1'],
|
||||||
'f1' => 'f1'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
// Make sure it allows returning document node:
|
// Make sure it allows returning document node:
|
||||||
$called = false;
|
$called = false;
|
||||||
$this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) {
|
$this->config->setPersistentQueryLoader(function ($queryId, OperationParams $params) use (&$called) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$this->assertEquals('some-id', $queryId);
|
$this->assertEquals('some-id', $queryId);
|
||||||
|
|
||||||
return Parser::parse('{f1}');
|
return Parser::parse('{f1}');
|
||||||
});
|
});
|
||||||
$result = $this->executePersistedQuery('some-id');
|
$result = $this->executePersistedQuery('some-id');
|
||||||
@ -334,7 +389,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
'Persistent query loader must return query string or instance of GraphQL\Language\AST\DocumentNode ' .
|
'Persistent query loader must return query string or instance of GraphQL\Language\AST\DocumentNode ' .
|
||||||
'but got: {"err":"err"}'
|
'but got: {"err":"err"}'
|
||||||
);
|
);
|
||||||
$this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) {
|
$this->config->setPersistentQueryLoader(function () {
|
||||||
return ['err' => 'err'];
|
return ['err' => 'err'];
|
||||||
});
|
});
|
||||||
$this->executePersistedQuery('some-id');
|
$this->executePersistedQuery('some-id');
|
||||||
@ -342,56 +397,55 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
public function testPersistedQueriesAreStillValidatedByDefault() : void
|
public function testPersistedQueriesAreStillValidatedByDefault() : void
|
||||||
{
|
{
|
||||||
$this->config->setPersistentQueryLoader(function() {
|
$this->config->setPersistentQueryLoader(function () {
|
||||||
return '{invalid}';
|
return '{invalid}';
|
||||||
});
|
});
|
||||||
$result = $this->executePersistedQuery('some-id');
|
$result = $this->executePersistedQuery('some-id');
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Cannot query field "invalid" on type "Query".',
|
'message' => 'Cannot query field "invalid" on type "Query".',
|
||||||
'locations' => [ ['line' => 1, 'column' => 2] ],
|
'locations' => [['line' => 1, 'column' => 2]],
|
||||||
'category' => 'graphql'
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAllowSkippingValidationForPersistedQueries() : void
|
public function testAllowSkippingValidationForPersistedQueries() : void
|
||||||
{
|
{
|
||||||
$this->config
|
$this->config
|
||||||
->setPersistentQueryLoader(function($queryId) {
|
->setPersistentQueryLoader(function ($queryId) {
|
||||||
if ($queryId === 'some-id') {
|
if ($queryId === 'some-id') {
|
||||||
return '{invalid}';
|
return '{invalid}';
|
||||||
} else {
|
|
||||||
return '{invalid2}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return '{invalid2}';
|
||||||
})
|
})
|
||||||
->setValidationRules(function(OperationParams $params) {
|
->setValidationRules(function (OperationParams $params) {
|
||||||
if ($params->queryId === 'some-id') {
|
if ($params->queryId === 'some-id') {
|
||||||
return [];
|
return [];
|
||||||
} else {
|
|
||||||
return DocumentValidator::allRules();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return DocumentValidator::allRules();
|
||||||
});
|
});
|
||||||
|
|
||||||
$result = $this->executePersistedQuery('some-id');
|
$result = $this->executePersistedQuery('some-id');
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => []
|
'data' => [],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
|
|
||||||
$result = $this->executePersistedQuery('some-other-id');
|
$result = $this->executePersistedQuery('some-other-id');
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'Cannot query field "invalid2" on type "Query".',
|
'message' => 'Cannot query field "invalid2" on type "Query".',
|
||||||
'locations' => [ ['line' => 1, 'column' => 2] ],
|
'locations' => [['line' => 1, 'column' => 2]],
|
||||||
'category' => 'graphql'
|
'category' => 'graphql',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expected, $result->toArray());
|
$this->assertEquals($expected, $result->toArray());
|
||||||
}
|
}
|
||||||
@ -400,7 +454,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
{
|
{
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
$this->expectExceptionMessage('Expecting validation rules to be array or callable returning array, but got: instance of stdClass');
|
$this->expectExceptionMessage('Expecting validation rules to be array or callable returning array, but got: instance of stdClass');
|
||||||
$this->config->setValidationRules(function(OperationParams $params) {
|
$this->config->setValidationRules(function (OperationParams $params) {
|
||||||
return new \stdClass();
|
return new \stdClass();
|
||||||
});
|
});
|
||||||
$this->executeQuery('{f1}');
|
$this->executeQuery('{f1}');
|
||||||
@ -411,28 +465,24 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$this->config->setQueryBatching(true);
|
$this->config->setQueryBatching(true);
|
||||||
|
|
||||||
$batch = [
|
$batch = [
|
||||||
|
['query' => '{invalid}'],
|
||||||
|
['query' => '{f1,fieldWithSafeException}'],
|
||||||
[
|
[
|
||||||
'query' => '{invalid}'
|
'query' => '
|
||||||
],
|
|
||||||
[
|
|
||||||
'query' => '{f1,fieldWithSafeException}'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'query' => '
|
|
||||||
query ($a: String!, $b: String!) {
|
query ($a: String!, $b: String!) {
|
||||||
a: fieldWithArg(arg: $a)
|
a: fieldWithArg(arg: $a)
|
||||||
b: fieldWithArg(arg: $b)
|
b: fieldWithArg(arg: $b)
|
||||||
}
|
}
|
||||||
',
|
',
|
||||||
'variables' => ['a' => 'a', 'b' => 'b'],
|
'variables' => ['a' => 'a', 'b' => 'b'],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = $this->executeBatchedQuery($batch);
|
$result = $this->executeBatchedQuery($batch);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'errors' => [['message' => 'Cannot query field "invalid" on type "Query".']]
|
'errors' => [['message' => 'Cannot query field "invalid" on type "Query".']],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'data' => [
|
'data' => [
|
||||||
@ -440,15 +490,15 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
'fieldWithSafeException' => null
|
'fieldWithSafeException' => null
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['message' => 'This is the exception we want']
|
['message' => 'This is the exception we want'],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'data' => [
|
'data' => [
|
||||||
'a' => 'a',
|
'a' => 'a',
|
||||||
'b' => 'b'
|
'b' => 'b',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected[0], $result[0]->toArray());
|
$this->assertArraySubset($expected[0], $result[0]->toArray());
|
||||||
@ -459,15 +509,9 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testDeferredsAreSharedAmongAllBatchedQueries() : void
|
public function testDeferredsAreSharedAmongAllBatchedQueries() : void
|
||||||
{
|
{
|
||||||
$batch = [
|
$batch = [
|
||||||
[
|
['query' => '{dfd(num: 1)}'],
|
||||||
'query' => '{dfd(num: 1)}'
|
['query' => '{dfd(num: 2)}'],
|
||||||
],
|
['query' => '{dfd(num: 3)}'],
|
||||||
[
|
|
||||||
'query' => '{dfd(num: 2)}'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'query' => '{dfd(num: 3)}',
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$calls = [];
|
$calls = [];
|
||||||
@ -476,13 +520,14 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
->setQueryBatching(true)
|
->setQueryBatching(true)
|
||||||
->setRootValue('1')
|
->setRootValue('1')
|
||||||
->setContext([
|
->setContext([
|
||||||
'buffer' => function($num) use (&$calls) {
|
'buffer' => function ($num) use (&$calls) {
|
||||||
$calls[] = "buffer: $num";
|
$calls[] = sprintf('buffer: %d', $num);
|
||||||
|
},
|
||||||
|
'load' => function ($num) use (&$calls) {
|
||||||
|
$calls[] = sprintf('load: %d', $num);
|
||||||
|
|
||||||
|
return sprintf('loaded: %d', $num);
|
||||||
},
|
},
|
||||||
'load' => function($num) use (&$calls) {
|
|
||||||
$calls[] = "load: $num";
|
|
||||||
return "loaded: $num";
|
|
||||||
}
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$result = $this->executeBatchedQuery($batch);
|
$result = $this->executeBatchedQuery($batch);
|
||||||
@ -499,19 +544,13 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
[
|
[
|
||||||
'data' => [
|
'data' => ['dfd' => 'loaded: 1'],
|
||||||
'dfd' => 'loaded: 1'
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'data' => [
|
'data' => ['dfd' => 'loaded: 2'],
|
||||||
'dfd' => 'loaded: 2'
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'data' => [
|
'data' => ['dfd' => 'loaded: 3'],
|
||||||
'dfd' => 'loaded: 3'
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -522,7 +561,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
public function testValidatesParamsBeforeExecution() : void
|
public function testValidatesParamsBeforeExecution() : void
|
||||||
{
|
{
|
||||||
$op = OperationParams::create(['queryBad' => '{f1}']);
|
$op = OperationParams::create(['queryBad' => '{f1}']);
|
||||||
$helper = new Helper();
|
$helper = new Helper();
|
||||||
$result = $helper->executeOperation($this->config, $op);
|
$result = $helper->executeOperation($this->config, $op);
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $result);
|
$this->assertInstanceOf(ExecutionResult::class, $result);
|
||||||
@ -546,10 +585,10 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$called = false;
|
$called = false;
|
||||||
$params = $doc = $operationType = null;
|
$params = $doc = $operationType = null;
|
||||||
|
|
||||||
$this->config->setContext(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
$this->config->setContext(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$params = $p;
|
$params = $p;
|
||||||
$doc = $d;
|
$doc = $d;
|
||||||
$operationType = $o;
|
$operationType = $o;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -566,10 +605,10 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$called = false;
|
$called = false;
|
||||||
$params = $doc = $operationType = null;
|
$params = $doc = $operationType = null;
|
||||||
|
|
||||||
$this->config->setRootValue(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
$this->config->setRootValue(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$params = $p;
|
$params = $p;
|
||||||
$doc = $d;
|
$doc = $d;
|
||||||
$operationType = $o;
|
$operationType = $o;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -584,20 +623,21 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
public function testAppliesErrorFormatter() : void
|
public function testAppliesErrorFormatter() : void
|
||||||
{
|
{
|
||||||
$called = false;
|
$called = false;
|
||||||
$error = null;
|
$error = null;
|
||||||
$this->config->setErrorFormatter(function($e) use (&$called, &$error) {
|
$this->config->setErrorFormatter(function ($e) use (&$called, &$error) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$error = $e;
|
$error = $e;
|
||||||
|
|
||||||
return ['test' => 'formatted'];
|
return ['test' => 'formatted'];
|
||||||
});
|
});
|
||||||
|
|
||||||
$result = $this->executeQuery('{fieldWithSafeException}');
|
$result = $this->executeQuery('{fieldWithSafeException}');
|
||||||
$this->assertFalse($called);
|
$this->assertFalse($called);
|
||||||
$formatted = $result->toArray();
|
$formatted = $result->toArray();
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['test' => 'formatted']
|
['test' => 'formatted'],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertTrue($called);
|
$this->assertTrue($called);
|
||||||
$this->assertArraySubset($expected, $formatted);
|
$this->assertArraySubset($expected, $formatted);
|
||||||
@ -605,28 +645,29 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
// Assert debugging still works even with custom formatter
|
// Assert debugging still works even with custom formatter
|
||||||
$formatted = $result->toArray(Debug::INCLUDE_TRACE);
|
$formatted = $result->toArray(Debug::INCLUDE_TRACE);
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'test' => 'formatted',
|
'test' => 'formatted',
|
||||||
'trace' => []
|
'trace' => [],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, $formatted);
|
$this->assertArraySubset($expected, $formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAppliesErrorsHandler() : void
|
public function testAppliesErrorsHandler() : void
|
||||||
{
|
{
|
||||||
$called = false;
|
$called = false;
|
||||||
$errors = null;
|
$errors = null;
|
||||||
$formatter = null;
|
$formatter = null;
|
||||||
$this->config->setErrorsHandler(function($e, $f) use (&$called, &$errors, &$formatter) {
|
$this->config->setErrorsHandler(function ($e, $f) use (&$called, &$errors, &$formatter) {
|
||||||
$called = true;
|
$called = true;
|
||||||
$errors = $e;
|
$errors = $e;
|
||||||
$formatter = $f;
|
$formatter = $f;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
['test' => 'handled']
|
['test' => 'handled'],
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -634,10 +675,10 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
$this->assertFalse($called);
|
$this->assertFalse($called);
|
||||||
$formatted = $result->toArray();
|
$formatted = $result->toArray();
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['test' => 'handled']
|
['test' => 'handled'],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertTrue($called);
|
$this->assertTrue($called);
|
||||||
$this->assertArraySubset($expected, $formatted);
|
$this->assertArraySubset($expected, $formatted);
|
||||||
@ -646,46 +687,4 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$this->assertInternalType('callable', $formatter);
|
$this->assertInternalType('callable', $formatter);
|
||||||
$this->assertArraySubset($expected, $formatted);
|
$this->assertArraySubset($expected, $formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function executePersistedQuery($queryId, $variables = null)
|
|
||||||
{
|
|
||||||
$op = OperationParams::create(['queryId' => $queryId, 'variables' => $variables]);
|
|
||||||
$helper = new Helper();
|
|
||||||
$result = $helper->executeOperation($this->config, $op);
|
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $result);
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeQuery($query, $variables = null, $readonly = false)
|
|
||||||
{
|
|
||||||
$op = OperationParams::create(['query' => $query, 'variables' => $variables], $readonly);
|
|
||||||
$helper = new Helper();
|
|
||||||
$result = $helper->executeOperation($this->config, $op);
|
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $result);
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeBatchedQuery(array $qs)
|
|
||||||
{
|
|
||||||
$batch = [];
|
|
||||||
foreach ($qs as $params) {
|
|
||||||
$batch[] = OperationParams::create($params);
|
|
||||||
}
|
|
||||||
$helper = new Helper();
|
|
||||||
$result = $helper->executeBatch($this->config, $batch);
|
|
||||||
$this->assertInternalType('array', $result);
|
|
||||||
$this->assertCount(count($qs), $result);
|
|
||||||
|
|
||||||
foreach ($result as $index => $entry) {
|
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $entry, "Result at $index is not an instance of " . ExecutionResult::class);
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertQueryResultEquals($expected, $query, $variables = null)
|
|
||||||
{
|
|
||||||
$result = $this->executeQuery($query, $variables);
|
|
||||||
$this->assertArraySubset($expected, $result->toArray(true));
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Server\Helper;
|
use GraphQL\Server\Helper;
|
||||||
use GraphQL\Server\OperationParams;
|
use GraphQL\Server\OperationParams;
|
||||||
@ -9,15 +11,17 @@ use GraphQL\Server\RequestError;
|
|||||||
use GraphQL\Tests\Server\Psr7\PsrRequestStub;
|
use GraphQL\Tests\Server\Psr7\PsrRequestStub;
|
||||||
use GraphQL\Tests\Server\Psr7\PsrStreamStub;
|
use GraphQL\Tests\Server\Psr7\PsrStreamStub;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function json_decode;
|
||||||
|
use function json_encode;
|
||||||
|
|
||||||
class RequestParsingTest extends TestCase
|
class RequestParsingTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testParsesGraphqlRequest() : void
|
public function testParsesGraphqlRequest() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawRequest('application/graphql', $query),
|
'raw' => $this->parseRawRequest('application/graphql', $query),
|
||||||
'psr' => $this->parsePsrRequest('application/graphql', $query)
|
'psr' => $this->parsePsrRequest('application/graphql', $query),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $source => $parsedBody) {
|
foreach ($parsed as $source => $parsedBody) {
|
||||||
@ -26,20 +30,91 @@ class RequestParsingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $contentType
|
||||||
|
* @param string $content
|
||||||
|
*
|
||||||
|
* @return OperationParams|OperationParams[]
|
||||||
|
*/
|
||||||
|
private function parseRawRequest($contentType, $content, string $method = 'POST')
|
||||||
|
{
|
||||||
|
$_SERVER['CONTENT_TYPE'] = $contentType;
|
||||||
|
$_SERVER['REQUEST_METHOD'] = $method;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parseHttpRequest(function () use ($content) {
|
||||||
|
return $content;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $contentType
|
||||||
|
* @param string $content
|
||||||
|
*
|
||||||
|
* @return OperationParams|OperationParams[]
|
||||||
|
*/
|
||||||
|
private function parsePsrRequest($contentType, $content, string $method = 'POST')
|
||||||
|
{
|
||||||
|
$psrRequestBody = new PsrStreamStub();
|
||||||
|
$psrRequestBody->content = $content;
|
||||||
|
|
||||||
|
$psrRequest = new PsrRequestStub();
|
||||||
|
$psrRequest->headers['content-type'] = [$contentType];
|
||||||
|
$psrRequest->method = $method;
|
||||||
|
$psrRequest->body = $psrRequestBody;
|
||||||
|
|
||||||
|
if ($contentType === 'application/json') {
|
||||||
|
$parsedBody = json_decode($content, true);
|
||||||
|
$parsedBody = $parsedBody === false ? null : $parsedBody;
|
||||||
|
} else {
|
||||||
|
$parsedBody = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$psrRequest->parsedBody = $parsedBody;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parsePsrRequest($psrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param OperationParams $params
|
||||||
|
* @param string $query
|
||||||
|
* @param string $queryId
|
||||||
|
* @param mixed|null $variables
|
||||||
|
* @param string $operation
|
||||||
|
*/
|
||||||
|
private function assertValidOperationParams(
|
||||||
|
$params,
|
||||||
|
$query,
|
||||||
|
$queryId = null,
|
||||||
|
$variables = null,
|
||||||
|
$operation = null,
|
||||||
|
$message = ''
|
||||||
|
) {
|
||||||
|
$this->assertInstanceOf(OperationParams::class, $params, $message);
|
||||||
|
|
||||||
|
$this->assertSame($query, $params->query, $message);
|
||||||
|
$this->assertSame($queryId, $params->queryId, $message);
|
||||||
|
$this->assertSame($variables, $params->variables, $message);
|
||||||
|
$this->assertSame($operation, $params->operation, $message);
|
||||||
|
}
|
||||||
|
|
||||||
public function testParsesUrlencodedRequest() : void
|
public function testParsesUrlencodedRequest() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$post = [
|
$post = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawFormUrlencodedRequest($post),
|
'raw' => $this->parseRawFormUrlencodedRequest($post),
|
||||||
'psr' => $this->parsePsrFormUrlEncodedRequest($post)
|
'psr' => $this->parsePsrFormUrlEncodedRequest($post),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
@ -48,20 +123,53 @@ class RequestParsingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $postValue
|
||||||
|
* @return OperationParams|OperationParams[]
|
||||||
|
*/
|
||||||
|
private function parseRawFormUrlencodedRequest($postValue)
|
||||||
|
{
|
||||||
|
$_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
|
$_POST = $postValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parseHttpRequest(function () {
|
||||||
|
throw new InvariantViolation("Shouldn't read from php://input for urlencoded request");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $postValue
|
||||||
|
* @return OperationParams[]|OperationParams
|
||||||
|
*/
|
||||||
|
private function parsePsrFormUrlEncodedRequest($postValue)
|
||||||
|
{
|
||||||
|
$psrRequest = new PsrRequestStub();
|
||||||
|
$psrRequest->headers['content-type'] = ['application/x-www-form-urlencoded'];
|
||||||
|
$psrRequest->method = 'POST';
|
||||||
|
$psrRequest->parsedBody = $postValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parsePsrRequest($psrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
public function testParsesGetRequest() : void
|
public function testParsesGetRequest() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$get = [
|
$get = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawGetRequest($get),
|
'raw' => $this->parseRawGetRequest($get),
|
||||||
'psr' => $this->parsePsrGetRequest($get)
|
'psr' => $this->parsePsrGetRequest($get),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
@ -70,20 +178,51 @@ class RequestParsingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $getValue
|
||||||
|
* @return OperationParams
|
||||||
|
*/
|
||||||
|
private function parseRawGetRequest($getValue)
|
||||||
|
{
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||||
|
$_GET = $getValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parseHttpRequest(function () {
|
||||||
|
throw new InvariantViolation("Shouldn't read from php://input for urlencoded request");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $getValue
|
||||||
|
* @return OperationParams[]|OperationParams
|
||||||
|
*/
|
||||||
|
private function parsePsrGetRequest($getValue)
|
||||||
|
{
|
||||||
|
$psrRequest = new PsrRequestStub();
|
||||||
|
$psrRequest->method = 'GET';
|
||||||
|
$psrRequest->queryParams = $getValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parsePsrRequest($psrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
public function testParsesMultipartFormdataRequest() : void
|
public function testParsesMultipartFormdataRequest() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$post = [
|
$post = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawMultipartFormdataRequest($post),
|
'raw' => $this->parseRawMultipartFormdataRequest($post),
|
||||||
'psr' => $this->parsePsrMultipartFormdataRequest($post)
|
'psr' => $this->parsePsrMultipartFormdataRequest($post),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
@ -92,20 +231,53 @@ class RequestParsingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $postValue
|
||||||
|
* @return OperationParams|OperationParams[]
|
||||||
|
*/
|
||||||
|
private function parseRawMultipartFormDataRequest($postValue)
|
||||||
|
{
|
||||||
|
$_SERVER['CONTENT_TYPE'] = 'multipart/form-data; boundary=----FormBoundary';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
|
$_POST = $postValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parseHttpRequest(function () {
|
||||||
|
throw new InvariantViolation("Shouldn't read from php://input for multipart/form-data request");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $postValue
|
||||||
|
* @return OperationParams|OperationParams[]
|
||||||
|
*/
|
||||||
|
private function parsePsrMultipartFormDataRequest($postValue)
|
||||||
|
{
|
||||||
|
$psrRequest = new PsrRequestStub();
|
||||||
|
$psrRequest->headers['content-type'] = ['multipart/form-data; boundary=----FormBoundary'];
|
||||||
|
$psrRequest->method = 'POST';
|
||||||
|
$psrRequest->parsedBody = $postValue;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parsePsrRequest($psrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
public function testParsesJSONRequest() : void
|
public function testParsesJSONRequest() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$body = [
|
$body = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body))
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
$this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
|
$this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
|
||||||
@ -115,18 +287,18 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
public function testParsesVariablesAsJSON() : void
|
public function testParsesVariablesAsJSON() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = ['test' => 1, 'test2' => 2];
|
$variables = ['test' => 1, 'test2' => 2];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$body = [
|
$body = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => json_encode($variables),
|
'variables' => json_encode($variables),
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body))
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
$this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
|
$this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method);
|
||||||
@ -136,14 +308,14 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
public function testIgnoresInvalidVariablesJson() : void
|
public function testIgnoresInvalidVariablesJson() : void
|
||||||
{
|
{
|
||||||
$query = '{my query}';
|
$query = '{my query}';
|
||||||
$variables = '"some invalid json';
|
$variables = '"some invalid json';
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$body = [
|
$body = [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation
|
'operationName' => $operation,
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
||||||
@ -157,27 +329,41 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
public function testParsesBatchJSONRequest() : void
|
public function testParsesBatchJSONRequest() : void
|
||||||
{
|
{
|
||||||
$body = [
|
$body = [
|
||||||
[
|
[
|
||||||
'query' => '{my query}',
|
'query' => '{my query}',
|
||||||
'variables' => ['test' => 1, 'test2' => 2],
|
'variables' => ['test' => 1, 'test2' => 2],
|
||||||
'operationName' => 'op'
|
'operationName' => 'op',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'queryId' => 'my-query-id',
|
'queryId' => 'my-query-id',
|
||||||
'variables' => ['test' => 1, 'test2' => 2],
|
'variables' => ['test' => 1, 'test2' => 2],
|
||||||
'operationName' => 'op2'
|
'operationName' => 'op2',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$parsed = [
|
$parsed = [
|
||||||
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
'raw' => $this->parseRawRequest('application/json', json_encode($body)),
|
||||||
'psr' => $this->parsePsrRequest('application/json', json_encode($body))
|
'psr' => $this->parsePsrRequest('application/json', json_encode($body)),
|
||||||
];
|
];
|
||||||
foreach ($parsed as $method => $parsedBody) {
|
foreach ($parsed as $method => $parsedBody) {
|
||||||
$this->assertInternalType('array', $parsedBody, $method);
|
$this->assertInternalType('array', $parsedBody, $method);
|
||||||
$this->assertCount(2, $parsedBody, $method);
|
$this->assertCount(2, $parsedBody, $method);
|
||||||
$this->assertValidOperationParams($parsedBody[0], $body[0]['query'], null, $body[0]['variables'], $body[0]['operationName'], $method);
|
$this->assertValidOperationParams(
|
||||||
$this->assertValidOperationParams($parsedBody[1], null, $body[1]['queryId'], $body[1]['variables'], $body[1]['operationName'], $method);
|
$parsedBody[0],
|
||||||
|
$body[0]['query'],
|
||||||
|
null,
|
||||||
|
$body[0]['variables'],
|
||||||
|
$body[0]['operationName'],
|
||||||
|
$method
|
||||||
|
);
|
||||||
|
$this->assertValidOperationParams(
|
||||||
|
$parsedBody[1],
|
||||||
|
null,
|
||||||
|
$body[1]['queryId'],
|
||||||
|
$body[1]['variables'],
|
||||||
|
$body[1]['operationName'],
|
||||||
|
$method
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,8 +373,8 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('Could not parse JSON: Syntax error');
|
$this->expectExceptionMessage('Could not parse JSON: Syntax error');
|
||||||
$this->parseRawRequest('application/json', $body);
|
$this->parseRawRequest('application/json', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsParsingInvalidRawJsonRequestPsr() : void
|
public function testFailsParsingInvalidRawJsonRequestPsr() : void
|
||||||
{
|
{
|
||||||
@ -196,7 +382,7 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
$this->expectExceptionMessage('PSR-7 request is expected to provide parsed body for "application/json" requests but got null');
|
$this->expectExceptionMessage('PSR-7 request is expected to provide parsed body for "application/json" requests but got null');
|
||||||
$this->parsePsrRequest('application/json', $body);
|
$this->parsePsrRequest('application/json', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsParsingNonPreParsedPsrRequest() : void
|
public function testFailsParsingNonPreParsedPsrRequest() : void
|
||||||
@ -222,8 +408,8 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"');
|
$this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"');
|
||||||
$this->parseRawRequest('application/json', $body);
|
$this->parseRawRequest('application/json', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsParsingNonArrayOrObjectJsonRequestPsr() : void
|
public function testFailsParsingNonArrayOrObjectJsonRequestPsr() : void
|
||||||
{
|
{
|
||||||
@ -231,13 +417,13 @@ class RequestParsingTest extends TestCase
|
|||||||
|
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"');
|
$this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"');
|
||||||
$this->parsePsrRequest('application/json', $body);
|
$this->parsePsrRequest('application/json', $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsParsingInvalidContentTypeRaw() : void
|
public function testFailsParsingInvalidContentTypeRaw() : void
|
||||||
{
|
{
|
||||||
$contentType = 'not-supported-content-type';
|
$contentType = 'not-supported-content-type';
|
||||||
$body = 'test';
|
$body = 'test';
|
||||||
|
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"');
|
$this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"');
|
||||||
@ -247,194 +433,38 @@ class RequestParsingTest extends TestCase
|
|||||||
public function testFailsParsingInvalidContentTypePsr() : void
|
public function testFailsParsingInvalidContentTypePsr() : void
|
||||||
{
|
{
|
||||||
$contentType = 'not-supported-content-type';
|
$contentType = 'not-supported-content-type';
|
||||||
$body = 'test';
|
$body = 'test';
|
||||||
|
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"');
|
$this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"');
|
||||||
$this->parseRawRequest($contentType, $body);
|
$this->parseRawRequest($contentType, $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsWithMissingContentTypeRaw() : void
|
public function testFailsWithMissingContentTypeRaw() : void
|
||||||
{
|
{
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('Missing "Content-Type" header');
|
$this->expectExceptionMessage('Missing "Content-Type" header');
|
||||||
$this->parseRawRequest(null, 'test');
|
$this->parseRawRequest(null, 'test');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsWithMissingContentTypePsr() : void
|
public function testFailsWithMissingContentTypePsr() : void
|
||||||
{
|
{
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('Missing "Content-Type" header');
|
$this->expectExceptionMessage('Missing "Content-Type" header');
|
||||||
$this->parsePsrRequest(null, 'test');
|
$this->parsePsrRequest(null, 'test');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsOnMethodsOtherThanPostOrGetRaw() : void
|
public function testFailsOnMethodsOtherThanPostOrGetRaw() : void
|
||||||
{
|
{
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('HTTP Method "PUT" is not supported');
|
$this->expectExceptionMessage('HTTP Method "PUT" is not supported');
|
||||||
$this->parseRawRequest('application/json', json_encode([]), "PUT");
|
$this->parseRawRequest('application/json', json_encode([]), 'PUT');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailsOnMethodsOtherThanPostOrGetPsr() : void
|
public function testFailsOnMethodsOtherThanPostOrGetPsr() : void
|
||||||
{
|
{
|
||||||
$this->expectException(RequestError::class);
|
$this->expectException(RequestError::class);
|
||||||
$this->expectExceptionMessage('HTTP Method "PUT" is not supported');
|
$this->expectExceptionMessage('HTTP Method "PUT" is not supported');
|
||||||
$this->parsePsrRequest('application/json', json_encode([]), "PUT");
|
$this->parsePsrRequest('application/json', json_encode([]), 'PUT');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $contentType
|
|
||||||
* @param string $content
|
|
||||||
* @param $method
|
|
||||||
*
|
|
||||||
* @return OperationParams|OperationParams[]
|
|
||||||
*/
|
|
||||||
private function parseRawRequest($contentType, $content, $method = 'POST')
|
|
||||||
{
|
|
||||||
$_SERVER['CONTENT_TYPE'] = $contentType;
|
|
||||||
$_SERVER['REQUEST_METHOD'] = $method;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parseHttpRequest(function() use ($content) {
|
|
||||||
return $content;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $contentType
|
|
||||||
* @param string $content
|
|
||||||
* @param $method
|
|
||||||
*
|
|
||||||
* @return OperationParams|OperationParams[]
|
|
||||||
*/
|
|
||||||
private function parsePsrRequest($contentType, $content, $method = 'POST')
|
|
||||||
{
|
|
||||||
$psrRequestBody = new PsrStreamStub();
|
|
||||||
$psrRequestBody->content = $content;
|
|
||||||
|
|
||||||
$psrRequest = new PsrRequestStub();
|
|
||||||
$psrRequest->headers['content-type'] = [$contentType];
|
|
||||||
$psrRequest->method = $method;
|
|
||||||
$psrRequest->body = $psrRequestBody;
|
|
||||||
|
|
||||||
if ($contentType === 'application/json') {
|
|
||||||
$parsedBody = json_decode($content, true);
|
|
||||||
$parsedBody = $parsedBody === false ? null : $parsedBody;
|
|
||||||
} else {
|
|
||||||
$parsedBody = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$psrRequest->parsedBody = $parsedBody;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parsePsrRequest($psrRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $postValue
|
|
||||||
* @return OperationParams|OperationParams[]
|
|
||||||
*/
|
|
||||||
private function parseRawFormUrlencodedRequest($postValue)
|
|
||||||
{
|
|
||||||
$_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
$_POST = $postValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parseHttpRequest(function() {
|
|
||||||
throw new InvariantViolation("Shouldn't read from php://input for urlencoded request");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $postValue
|
|
||||||
* @return array|Helper
|
|
||||||
*/
|
|
||||||
private function parsePsrFormUrlEncodedRequest($postValue)
|
|
||||||
{
|
|
||||||
$psrRequest = new PsrRequestStub();
|
|
||||||
$psrRequest->headers['content-type'] = ['application/x-www-form-urlencoded'];
|
|
||||||
$psrRequest->method = 'POST';
|
|
||||||
$psrRequest->parsedBody = $postValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parsePsrRequest($psrRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $postValue
|
|
||||||
* @return OperationParams|OperationParams[]
|
|
||||||
*/
|
|
||||||
private function parseRawMultipartFormDataRequest($postValue)
|
|
||||||
{
|
|
||||||
$_SERVER['CONTENT_TYPE'] = 'multipart/form-data; boundary=----FormBoundary';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
$_POST = $postValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parseHttpRequest(function() {
|
|
||||||
throw new InvariantViolation("Shouldn't read from php://input for multipart/form-data request");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $postValue
|
|
||||||
* @return array|Helper
|
|
||||||
*/
|
|
||||||
private function parsePsrMultipartFormDataRequest($postValue)
|
|
||||||
{
|
|
||||||
$psrRequest = new PsrRequestStub();
|
|
||||||
$psrRequest->headers['content-type'] = ['multipart/form-data; boundary=----FormBoundary'];
|
|
||||||
$psrRequest->method = 'POST';
|
|
||||||
$psrRequest->parsedBody = $postValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parsePsrRequest($psrRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $getValue
|
|
||||||
* @return OperationParams
|
|
||||||
*/
|
|
||||||
private function parseRawGetRequest($getValue)
|
|
||||||
{
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
|
||||||
$_GET = $getValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parseHttpRequest(function() {
|
|
||||||
throw new InvariantViolation("Shouldn't read from php://input for urlencoded request");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $getValue
|
|
||||||
* @return array|Helper
|
|
||||||
*/
|
|
||||||
private function parsePsrGetRequest($getValue)
|
|
||||||
{
|
|
||||||
$psrRequest = new PsrRequestStub();
|
|
||||||
$psrRequest->method = 'GET';
|
|
||||||
$psrRequest->queryParams = $getValue;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parsePsrRequest($psrRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param OperationParams $params
|
|
||||||
* @param string $query
|
|
||||||
* @param string $queryId
|
|
||||||
* @param array $variables
|
|
||||||
* @param string $operation
|
|
||||||
*/
|
|
||||||
private function assertValidOperationParams($params, $query, $queryId = null, $variables = null, $operation = null, $message = '')
|
|
||||||
{
|
|
||||||
$this->assertInstanceOf(OperationParams::class, $params, $message);
|
|
||||||
|
|
||||||
$this->assertSame($query, $params->query, $message);
|
|
||||||
$this->assertSame($queryId, $params->queryId, $message);
|
|
||||||
$this->assertSame($variables, $params->variables, $message);
|
|
||||||
$this->assertSame($operation, $params->operation, $message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Server\Helper;
|
use GraphQL\Server\Helper;
|
||||||
@ -9,28 +12,35 @@ class RequestValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
public function testSimpleRequestShouldValidate() : void
|
public function testSimpleRequestShouldValidate() : void
|
||||||
{
|
{
|
||||||
$query = '{my q}';
|
$query = '{my q}';
|
||||||
$variables = ['a' => 'b', 'c' => 'd'];
|
$variables = ['a' => 'b', 'c' => 'd'];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation,
|
'operationName' => $operation,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertValid($parsedBody);
|
$this->assertValid($parsedBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertValid($parsedRequest)
|
||||||
|
{
|
||||||
|
$helper = new Helper();
|
||||||
|
$errors = $helper->validateOperationParams($parsedRequest);
|
||||||
|
$this->assertEmpty($errors, isset($errors[0]) ? $errors[0]->getMessage() : '');
|
||||||
|
}
|
||||||
|
|
||||||
public function testRequestWithQueryIdShouldValidate() : void
|
public function testRequestWithQueryIdShouldValidate() : void
|
||||||
{
|
{
|
||||||
$queryId = 'some-query-id';
|
$queryId = 'some-query-id';
|
||||||
$variables = ['a' => 'b', 'c' => 'd'];
|
$variables = ['a' => 'b', 'c' => 'd'];
|
||||||
$operation = 'op';
|
$operation = 'op';
|
||||||
|
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'queryId' => $queryId,
|
'queryId' => $queryId,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
'operationName' => $operation,
|
'operationName' => $operation,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -40,7 +50,7 @@ class RequestValidationTest extends TestCase
|
|||||||
public function testRequiresQueryOrQueryId() : void
|
public function testRequiresQueryOrQueryId() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'variables' => ['foo' => 'bar'],
|
'variables' => ['foo' => 'bar'],
|
||||||
'operationName' => 'op',
|
'operationName' => 'op',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -50,10 +60,21 @@ class RequestValidationTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function assertInputError($parsedRequest, $expectedMessage)
|
||||||
|
{
|
||||||
|
$helper = new Helper();
|
||||||
|
$errors = $helper->validateOperationParams($parsedRequest);
|
||||||
|
if (! empty($errors[0])) {
|
||||||
|
$this->assertEquals($expectedMessage, $errors[0]->getMessage());
|
||||||
|
} else {
|
||||||
|
$this->fail('Expected error not returned');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testFailsWhenBothQueryAndQueryIdArePresent() : void
|
public function testFailsWhenBothQueryAndQueryIdArePresent() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => '{my query}',
|
'query' => '{my query}',
|
||||||
'queryId' => 'my-query-id',
|
'queryId' => 'my-query-id',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -66,7 +87,7 @@ class RequestValidationTest extends TestCase
|
|||||||
public function testFailsWhenQueryParameterIsNotString() : void
|
public function testFailsWhenQueryParameterIsNotString() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => ['t' => '{my query}']
|
'query' => ['t' => '{my query}'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertInputError(
|
$this->assertInputError(
|
||||||
@ -78,7 +99,7 @@ class RequestValidationTest extends TestCase
|
|||||||
public function testFailsWhenQueryIdParameterIsNotString() : void
|
public function testFailsWhenQueryIdParameterIsNotString() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'queryId' => ['t' => '{my query}']
|
'queryId' => ['t' => '{my query}'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertInputError(
|
$this->assertInputError(
|
||||||
@ -90,8 +111,8 @@ class RequestValidationTest extends TestCase
|
|||||||
public function testFailsWhenOperationParameterIsNotString() : void
|
public function testFailsWhenOperationParameterIsNotString() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => '{my query}',
|
'query' => '{my query}',
|
||||||
'operationName' => []
|
'operationName' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertInputError(
|
$this->assertInputError(
|
||||||
@ -105,17 +126,17 @@ class RequestValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testIgnoresNullAndEmptyStringVariables() : void
|
public function testIgnoresNullAndEmptyStringVariables() : void
|
||||||
{
|
{
|
||||||
$query = '{my q}';
|
$query = '{my q}';
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => null
|
'variables' => null,
|
||||||
]);
|
]);
|
||||||
$this->assertValid($parsedBody);
|
$this->assertValid($parsedBody);
|
||||||
|
|
||||||
$variables = "";
|
$variables = '';
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'variables' => $variables
|
'variables' => $variables,
|
||||||
]);
|
]);
|
||||||
$this->assertValid($parsedBody);
|
$this->assertValid($parsedBody);
|
||||||
}
|
}
|
||||||
@ -123,8 +144,8 @@ class RequestValidationTest extends TestCase
|
|||||||
public function testFailsWhenVariablesParameterIsNotObject() : void
|
public function testFailsWhenVariablesParameterIsNotObject() : void
|
||||||
{
|
{
|
||||||
$parsedBody = OperationParams::create([
|
$parsedBody = OperationParams::create([
|
||||||
'query' => '{my query}',
|
'query' => '{my query}',
|
||||||
'variables' => 0
|
'variables' => 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertInputError(
|
$this->assertInputError(
|
||||||
@ -132,22 +153,4 @@ class RequestValidationTest extends TestCase
|
|||||||
'GraphQL Request parameter "variables" must be object or JSON string parsed to object, but got 0'
|
'GraphQL Request parameter "variables" must be object or JSON string parsed to object, but got 0'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertValid($parsedRequest)
|
|
||||||
{
|
|
||||||
$helper = new Helper();
|
|
||||||
$errors = $helper->validateOperationParams($parsedRequest);
|
|
||||||
$this->assertEmpty($errors, isset($errors[0]) ? $errors[0]->getMessage() : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertInputError($parsedRequest, $expectedMessage)
|
|
||||||
{
|
|
||||||
$helper = new Helper();
|
|
||||||
$errors = $helper->validateOperationParams($parsedRequest);
|
|
||||||
if (!empty($errors[0])) {
|
|
||||||
$this->assertEquals($expectedMessage, $errors[0]->getMessage());
|
|
||||||
} else {
|
|
||||||
$this->fail('Expected error not returned');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Server\ServerConfig;
|
use GraphQL\Server\ServerConfig;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ServerConfigTest extends TestCase
|
class ServerConfigTest extends TestCase
|
||||||
@ -14,17 +17,17 @@ class ServerConfigTest extends TestCase
|
|||||||
public function testDefaults() : void
|
public function testDefaults() : void
|
||||||
{
|
{
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
$this->assertEquals(null, $config->getSchema());
|
$this->assertNull($config->getSchema());
|
||||||
$this->assertEquals(null, $config->getContext());
|
$this->assertNull($config->getContext());
|
||||||
$this->assertEquals(null, $config->getRootValue());
|
$this->assertNull($config->getRootValue());
|
||||||
$this->assertEquals(null, $config->getErrorFormatter());
|
$this->assertNull($config->getErrorFormatter());
|
||||||
$this->assertEquals(null, $config->getErrorsHandler());
|
$this->assertNull($config->getErrorsHandler());
|
||||||
$this->assertEquals(null, $config->getPromiseAdapter());
|
$this->assertNull($config->getPromiseAdapter());
|
||||||
$this->assertEquals(null, $config->getValidationRules());
|
$this->assertNull($config->getValidationRules());
|
||||||
$this->assertEquals(null, $config->getFieldResolver());
|
$this->assertNull($config->getFieldResolver());
|
||||||
$this->assertEquals(null, $config->getPersistentQueryLoader());
|
$this->assertNull($config->getPersistentQueryLoader());
|
||||||
$this->assertEquals(false, $config->getDebug());
|
$this->assertFalse($config->getDebug());
|
||||||
$this->assertEquals(false, $config->getQueryBatching());
|
$this->assertFalse($config->getQueryBatching());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAllowsSettingSchema() : void
|
public function testAllowsSettingSchema() : void
|
||||||
@ -70,7 +73,8 @@ class ServerConfigTest extends TestCase
|
|||||||
{
|
{
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$formatter = function() {};
|
$formatter = function () {
|
||||||
|
};
|
||||||
$config->setErrorFormatter($formatter);
|
$config->setErrorFormatter($formatter);
|
||||||
$this->assertSame($formatter, $config->getErrorFormatter());
|
$this->assertSame($formatter, $config->getErrorFormatter());
|
||||||
|
|
||||||
@ -83,7 +87,8 @@ class ServerConfigTest extends TestCase
|
|||||||
{
|
{
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$handler = function() {};
|
$handler = function () {
|
||||||
|
};
|
||||||
$config->setErrorsHandler($handler);
|
$config->setErrorsHandler($handler);
|
||||||
$this->assertSame($handler, $config->getErrorsHandler());
|
$this->assertSame($handler, $config->getErrorsHandler());
|
||||||
|
|
||||||
@ -113,11 +118,17 @@ class ServerConfigTest extends TestCase
|
|||||||
$config->setValidationRules($rules);
|
$config->setValidationRules($rules);
|
||||||
$this->assertSame($rules, $config->getValidationRules());
|
$this->assertSame($rules, $config->getValidationRules());
|
||||||
|
|
||||||
$rules = [function() {}];
|
$rules = [function () {
|
||||||
|
},
|
||||||
|
];
|
||||||
$config->setValidationRules($rules);
|
$config->setValidationRules($rules);
|
||||||
$this->assertSame($rules, $config->getValidationRules());
|
$this->assertSame($rules, $config->getValidationRules());
|
||||||
|
|
||||||
$rules = function() {return [function() {}];};
|
$rules = function () {
|
||||||
|
return [function () {
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
$config->setValidationRules($rules);
|
$config->setValidationRules($rules);
|
||||||
$this->assertSame($rules, $config->getValidationRules());
|
$this->assertSame($rules, $config->getValidationRules());
|
||||||
}
|
}
|
||||||
@ -126,7 +137,8 @@ class ServerConfigTest extends TestCase
|
|||||||
{
|
{
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$resolver = function() {};
|
$resolver = function () {
|
||||||
|
};
|
||||||
$config->setFieldResolver($resolver);
|
$config->setFieldResolver($resolver);
|
||||||
$this->assertSame($resolver, $config->getFieldResolver());
|
$this->assertSame($resolver, $config->getFieldResolver());
|
||||||
|
|
||||||
@ -139,7 +151,8 @@ class ServerConfigTest extends TestCase
|
|||||||
{
|
{
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$loader = function() {};
|
$loader = function () {
|
||||||
|
};
|
||||||
$config->setPersistentQueryLoader($loader);
|
$config->setPersistentQueryLoader($loader);
|
||||||
$this->assertSame($loader, $config->getPersistentQueryLoader());
|
$this->assertSame($loader, $config->getPersistentQueryLoader());
|
||||||
|
|
||||||
@ -153,27 +166,32 @@ class ServerConfigTest extends TestCase
|
|||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$config->setDebug(true);
|
$config->setDebug(true);
|
||||||
$this->assertSame(true, $config->getDebug());
|
$this->assertTrue($config->getDebug());
|
||||||
|
|
||||||
$config->setDebug(false);
|
$config->setDebug(false);
|
||||||
$this->assertSame(false, $config->getDebug());
|
$this->assertFalse($config->getDebug());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcceptsArray() : void
|
public function testAcceptsArray() : void
|
||||||
{
|
{
|
||||||
$arr = [
|
$arr = [
|
||||||
'schema' => new \GraphQL\Type\Schema([
|
'schema' => new Schema([
|
||||||
'query' => new ObjectType(['name' => 't', 'fields' => ['a' => Type::string()]])
|
'query' => new ObjectType(['name' => 't', 'fields' => ['a' => Type::string()]]),
|
||||||
]),
|
]),
|
||||||
'context' => new \stdClass(),
|
'context' => new \stdClass(),
|
||||||
'rootValue' => new \stdClass(),
|
'rootValue' => new \stdClass(),
|
||||||
'errorFormatter' => function() {},
|
'errorFormatter' => function () {
|
||||||
'promiseAdapter' => new SyncPromiseAdapter(),
|
},
|
||||||
'validationRules' => [function() {}],
|
'promiseAdapter' => new SyncPromiseAdapter(),
|
||||||
'fieldResolver' => function() {},
|
'validationRules' => [function () {
|
||||||
'persistentQueryLoader' => function() {},
|
},
|
||||||
'debug' => true,
|
],
|
||||||
'queryBatching' => true,
|
'fieldResolver' => function () {
|
||||||
|
},
|
||||||
|
'persistentQueryLoader' => function () {
|
||||||
|
},
|
||||||
|
'debug' => true,
|
||||||
|
'queryBatching' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$config = ServerConfig::create($arr);
|
$config = ServerConfig::create($arr);
|
||||||
@ -186,15 +204,13 @@ class ServerConfigTest extends TestCase
|
|||||||
$this->assertSame($arr['validationRules'], $config->getValidationRules());
|
$this->assertSame($arr['validationRules'], $config->getValidationRules());
|
||||||
$this->assertSame($arr['fieldResolver'], $config->getFieldResolver());
|
$this->assertSame($arr['fieldResolver'], $config->getFieldResolver());
|
||||||
$this->assertSame($arr['persistentQueryLoader'], $config->getPersistentQueryLoader());
|
$this->assertSame($arr['persistentQueryLoader'], $config->getPersistentQueryLoader());
|
||||||
$this->assertSame(true, $config->getDebug());
|
$this->assertTrue($config->getDebug());
|
||||||
$this->assertSame(true, $config->getQueryBatching());
|
$this->assertTrue($config->getQueryBatching());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowsOnInvalidArrayKey() : void
|
public function testThrowsOnInvalidArrayKey() : void
|
||||||
{
|
{
|
||||||
$arr = [
|
$arr = ['missingKey' => 'value'];
|
||||||
'missingKey' => 'value'
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
$this->expectExceptionMessage('Unknown server config option "missingKey"');
|
$this->expectExceptionMessage('Unknown server config option "missingKey"');
|
||||||
@ -204,7 +220,7 @@ class ServerConfigTest extends TestCase
|
|||||||
|
|
||||||
public function testInvalidValidationRules() : void
|
public function testInvalidValidationRules() : void
|
||||||
{
|
{
|
||||||
$rules = new \stdClass();
|
$rules = new \stdClass();
|
||||||
$config = ServerConfig::create();
|
$config = ServerConfig::create();
|
||||||
|
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Server;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Error\ClientAware;
|
use GraphQL\Error\ClientAware;
|
||||||
@ -9,31 +11,36 @@ 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 trigger_error;
|
||||||
|
use const E_USER_DEPRECATED;
|
||||||
|
use const E_USER_NOTICE;
|
||||||
|
use const E_USER_WARNING;
|
||||||
|
|
||||||
abstract class ServerTestCase extends TestCase
|
abstract class ServerTestCase extends TestCase
|
||||||
{
|
{
|
||||||
protected function buildSchema()
|
protected function buildSchema()
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'f1' => [
|
'f1' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($root, $args, $context, $info) {
|
'resolve' => function ($root, $args, $context, $info) {
|
||||||
return $info->fieldName;
|
return $info->fieldName;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'fieldWithPhpError' => [
|
'fieldWithPhpError' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($root, $args, $context, $info) {
|
'resolve' => function ($root, $args, $context, $info) {
|
||||||
trigger_error('deprecated', E_USER_DEPRECATED);
|
trigger_error('deprecated', E_USER_DEPRECATED);
|
||||||
trigger_error('notice', E_USER_NOTICE);
|
trigger_error('notice', E_USER_NOTICE);
|
||||||
trigger_error('warning', E_USER_WARNING);
|
trigger_error('warning', E_USER_WARNING);
|
||||||
$a = [];
|
$a = [];
|
||||||
$a['test']; // should produce PHP notice
|
$a['test']; // should produce PHP notice
|
||||||
|
|
||||||
return $info->fieldName;
|
return $info->fieldName;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'fieldWithSafeException' => [
|
'fieldWithSafeException' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
@ -48,54 +55,56 @@ abstract class ServerTestCase extends TestCase
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'testContextAndRootValue' => [
|
'testContextAndRootValue' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($root, $args, $context, $info) {
|
'resolve' => function ($root, $args, $context, $info) {
|
||||||
$context->testedRootValue = $root;
|
$context->testedRootValue = $root;
|
||||||
|
|
||||||
return $info->fieldName;
|
return $info->fieldName;
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'fieldWithArg' => [
|
'fieldWithArg' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'arg' => [
|
'arg' => [
|
||||||
'type' => Type::nonNull(Type::string())
|
'type' => Type::nonNull(Type::string()),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'resolve' => function($root, $args) {
|
'resolve' => function ($root, $args) {
|
||||||
return $args['arg'];
|
return $args['arg'];
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'dfd' => [
|
'dfd' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'num' => [
|
'num' => [
|
||||||
'type' => Type::nonNull(Type::int())
|
'type' => Type::nonNull(Type::int()),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'resolve' => function($root, $args, $context) {
|
'resolve' => function ($root, $args, $context) {
|
||||||
$context['buffer']($args['num']);
|
$context['buffer']($args['num']);
|
||||||
|
|
||||||
return new Deferred(function() use ($args, $context) {
|
return new Deferred(function () use ($args, $context) {
|
||||||
return $context['load']($args['num']);
|
return $context['load']($args['num']);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]),
|
]),
|
||||||
'mutation' => new ObjectType([
|
'mutation' => new ObjectType([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'm1' => [
|
'm1' => [
|
||||||
'type' => new ObjectType([
|
'type' => new ObjectType([
|
||||||
'name' => 'TestMutation',
|
'name' => 'TestMutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'result' => Type::string()
|
'result' => Type::string(),
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
])
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
@ -6,72 +9,75 @@ use GraphQL\Server\Helper;
|
|||||||
use GraphQL\Server\ServerConfig;
|
use GraphQL\Server\ServerConfig;
|
||||||
use GraphQL\Server\StandardServer;
|
use GraphQL\Server\StandardServer;
|
||||||
use GraphQL\Tests\Server\Psr7\PsrRequestStub;
|
use GraphQL\Tests\Server\Psr7\PsrRequestStub;
|
||||||
use GraphQL\Tests\Server\Psr7\PsrStreamStub;
|
use function json_encode;
|
||||||
|
|
||||||
class StandardServerTest extends ServerTestCase
|
class StandardServerTest extends ServerTestCase
|
||||||
{
|
{
|
||||||
/**
|
/** @var ServerConfig */
|
||||||
* @var ServerConfig
|
|
||||||
*/
|
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$schema = $this->buildSchema();
|
$schema = $this->buildSchema();
|
||||||
$this->config = ServerConfig::create()
|
$this->config = ServerConfig::create()
|
||||||
->setSchema($schema);
|
->setSchema($schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSimpleRequestExecutionWithOutsideParsing() : void
|
public function testSimpleRequestExecutionWithOutsideParsing() : void
|
||||||
{
|
{
|
||||||
$body = json_encode([
|
$body = json_encode(['query' => '{f1}']);
|
||||||
'query' => '{f1}'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$parsedBody = $this->parseRawRequest('application/json', $body);
|
$parsedBody = $this->parseRawRequest('application/json', $body);
|
||||||
$server = new StandardServer($this->config);
|
$server = new StandardServer($this->config);
|
||||||
|
|
||||||
$result = $server->executeRequest($parsedBody);
|
$result = $server->executeRequest($parsedBody);
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['f1' => 'f1'],
|
||||||
'f1' => 'f1',
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expected, $result->toArray(true));
|
$this->assertEquals($expected, $result->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function parseRawRequest($contentType, $content, $method = 'POST')
|
||||||
|
{
|
||||||
|
$_SERVER['CONTENT_TYPE'] = $contentType;
|
||||||
|
$_SERVER['REQUEST_METHOD'] = $method;
|
||||||
|
|
||||||
|
$helper = new Helper();
|
||||||
|
|
||||||
|
return $helper->parseHttpRequest(function () use ($content) {
|
||||||
|
return $content;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function testSimplePsrRequestExecution() : void
|
public function testSimplePsrRequestExecution() : void
|
||||||
{
|
{
|
||||||
$body = [
|
$body = ['query' => '{f1}'];
|
||||||
'query' => '{f1}'
|
|
||||||
];
|
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => ['f1' => 'f1'],
|
||||||
'f1' => 'f1'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$request = $this->preparePsrRequest('application/json', $body);
|
$request = $this->preparePsrRequest('application/json', $body);
|
||||||
$this->assertPsrRequestEquals($expected, $request);
|
$this->assertPsrRequestEquals($expected, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMultipleOperationPsrRequestExecution() : void
|
private function preparePsrRequest($contentType, $parsedBody)
|
||||||
{
|
{
|
||||||
$body = [
|
$psrRequest = new PsrRequestStub();
|
||||||
'query' => 'query firstOp {fieldWithPhpError} query secondOp {f1}',
|
$psrRequest->headers['content-type'] = [$contentType];
|
||||||
'operationName' => 'secondOp'
|
$psrRequest->method = 'POST';
|
||||||
];
|
$psrRequest->parsedBody = $parsedBody;
|
||||||
|
|
||||||
$expected = [
|
return $psrRequest;
|
||||||
'data' => [
|
}
|
||||||
'f1' => 'f1'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
$request = $this->preparePsrRequest('application/json', $body);
|
private function assertPsrRequestEquals($expected, $request)
|
||||||
$this->assertPsrRequestEquals($expected, $request);
|
{
|
||||||
|
$result = $this->executePsrRequest($request);
|
||||||
|
$this->assertArraySubset($expected, $result->toArray(true));
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function executePsrRequest($psrRequest)
|
private function executePsrRequest($psrRequest)
|
||||||
@ -79,33 +85,22 @@ class StandardServerTest extends ServerTestCase
|
|||||||
$server = new StandardServer($this->config);
|
$server = new StandardServer($this->config);
|
||||||
$result = $server->executePsrRequest($psrRequest);
|
$result = $server->executePsrRequest($psrRequest);
|
||||||
$this->assertInstanceOf(ExecutionResult::class, $result);
|
$this->assertInstanceOf(ExecutionResult::class, $result);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertPsrRequestEquals($expected, $request)
|
public function testMultipleOperationPsrRequestExecution() : void
|
||||||
{
|
{
|
||||||
$result = $this->executePsrRequest($request);
|
$body = [
|
||||||
$this->assertArraySubset($expected, $result->toArray(true));
|
'query' => 'query firstOp {fieldWithPhpError} query secondOp {f1}',
|
||||||
return $result;
|
'operationName' => 'secondOp',
|
||||||
}
|
];
|
||||||
|
|
||||||
private function preparePsrRequest($contentType, $parsedBody)
|
$expected = [
|
||||||
{
|
'data' => ['f1' => 'f1'],
|
||||||
$psrRequest = new PsrRequestStub();
|
];
|
||||||
$psrRequest->headers['content-type'] = [$contentType];
|
|
||||||
$psrRequest->method = 'POST';
|
|
||||||
$psrRequest->parsedBody = $parsedBody;
|
|
||||||
return $psrRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function parseRawRequest($contentType, $content, $method = 'POST')
|
$request = $this->preparePsrRequest('application/json', $body);
|
||||||
{
|
$this->assertPsrRequestEquals($expected, $request);
|
||||||
$_SERVER['CONTENT_TYPE'] = $contentType;
|
|
||||||
$_SERVER['REQUEST_METHOD'] = $method;
|
|
||||||
|
|
||||||
$helper = new Helper();
|
|
||||||
return $helper->parseHttpRequest(function() use ($content) {
|
|
||||||
return $content;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,31 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests;
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
|
use function array_map;
|
||||||
|
|
||||||
class StarWarsData
|
class StarWarsData
|
||||||
{
|
{
|
||||||
private static function luke()
|
/**
|
||||||
|
* Helper function to get a character by ID.
|
||||||
|
*/
|
||||||
|
public static function getCharacter($id)
|
||||||
{
|
{
|
||||||
return [
|
$humans = self::humans();
|
||||||
'id' => '1000',
|
$droids = self::droids();
|
||||||
'name' => 'Luke Skywalker',
|
if (isset($humans[$id])) {
|
||||||
'friends' => ['1002', '1003', '2000', '2001'],
|
return $humans[$id];
|
||||||
'appearsIn' => [4, 5, 6],
|
}
|
||||||
'homePlanet' => 'Tatooine',
|
if (isset($droids[$id])) {
|
||||||
];
|
return $droids[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function vader()
|
public static function humans()
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => '1001',
|
|
||||||
'name' => 'Darth Vader',
|
|
||||||
'friends' => ['1004'],
|
|
||||||
'appearsIn' => [4, 5, 6],
|
|
||||||
'homePlanet' => 'Tatooine',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function han()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => '1002',
|
|
||||||
'name' => 'Han Solo',
|
|
||||||
'friends' => ['1000', '1003', '2001'],
|
|
||||||
'appearsIn' => [4, 5, 6],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function leia()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => '1003',
|
|
||||||
'name' => 'Leia Organa',
|
|
||||||
'friends' => ['1000', '1002', '2000', '2001'],
|
|
||||||
'appearsIn' => [4, 5, 6],
|
|
||||||
'homePlanet' => 'Alderaan',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function tarkin()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => '1004',
|
|
||||||
'name' => 'Wilhuff Tarkin',
|
|
||||||
'friends' => ['1001'],
|
|
||||||
'appearsIn' => [4],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static function humans()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'1000' => self::luke(),
|
'1000' => self::luke(),
|
||||||
@ -67,13 +36,74 @@ class StarWarsData
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function luke()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => '1000',
|
||||||
|
'name' => 'Luke Skywalker',
|
||||||
|
'friends' => ['1002', '1003', '2000', '2001'],
|
||||||
|
'appearsIn' => [4, 5, 6],
|
||||||
|
'homePlanet' => 'Tatooine',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function vader()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => '1001',
|
||||||
|
'name' => 'Darth Vader',
|
||||||
|
'friends' => ['1004'],
|
||||||
|
'appearsIn' => [4, 5, 6],
|
||||||
|
'homePlanet' => 'Tatooine',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function han()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => '1002',
|
||||||
|
'name' => 'Han Solo',
|
||||||
|
'friends' => ['1000', '1003', '2001'],
|
||||||
|
'appearsIn' => [4, 5, 6],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function leia()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => '1003',
|
||||||
|
'name' => 'Leia Organa',
|
||||||
|
'friends' => ['1000', '1002', '2000', '2001'],
|
||||||
|
'appearsIn' => [4, 5, 6],
|
||||||
|
'homePlanet' => 'Alderaan',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function tarkin()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => '1004',
|
||||||
|
'name' => 'Wilhuff Tarkin',
|
||||||
|
'friends' => ['1001'],
|
||||||
|
'appearsIn' => [4],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function droids()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'2000' => self::threepio(),
|
||||||
|
'2001' => self::artoo(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private static function threepio()
|
private static function threepio()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => '2000',
|
'id' => '2000',
|
||||||
'name' => 'C-3PO',
|
'name' => 'C-3PO',
|
||||||
'friends' => ['1000', '1002', '1003', '2001'],
|
'friends' => ['1000', '1002', '1003', '2001'],
|
||||||
'appearsIn' => [4, 5, 6],
|
'appearsIn' => [4, 5, 6],
|
||||||
'primaryFunction' => 'Protocol',
|
'primaryFunction' => 'Protocol',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -82,81 +112,60 @@ class StarWarsData
|
|||||||
* We export artoo directly because the schema returns him
|
* We export artoo directly because the schema returns him
|
||||||
* from a root field, and hence needs to reference him.
|
* from a root field, and hence needs to reference him.
|
||||||
*/
|
*/
|
||||||
static function artoo()
|
private static function artoo()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'id' => '2001',
|
'id' => '2001',
|
||||||
'name' => 'R2-D2',
|
'name' => 'R2-D2',
|
||||||
'friends' => ['1000', '1002', '1003'],
|
'friends' => ['1000', '1002', '1003'],
|
||||||
'appearsIn' => [4, 5, 6],
|
'appearsIn' => [4, 5, 6],
|
||||||
'primaryFunction' => 'Astromech',
|
'primaryFunction' => 'Astromech',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static function droids()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'2000' => self::threepio(),
|
|
||||||
'2001' => self::artoo(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to get a character by ID.
|
|
||||||
*/
|
|
||||||
static function getCharacter($id)
|
|
||||||
{
|
|
||||||
$humans = self::humans();
|
|
||||||
$droids = self::droids();
|
|
||||||
if (isset($humans[$id])) {
|
|
||||||
return $humans[$id];
|
|
||||||
}
|
|
||||||
if (isset($droids[$id])) {
|
|
||||||
return $droids[$id];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows us to query for a character's friends.
|
* Allows us to query for a character's friends.
|
||||||
*/
|
*/
|
||||||
static function getFriends($character)
|
public static function getFriends($character)
|
||||||
{
|
{
|
||||||
return array_map([__CLASS__, 'getCharacter'], $character['friends']);
|
return array_map([__CLASS__, 'getCharacter'], $character['friends']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $episode
|
* @param int $episode
|
||||||
* @return array
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
static function getHero($episode)
|
public static function getHero($episode)
|
||||||
{
|
{
|
||||||
if ($episode === 5) {
|
if ($episode === 5) {
|
||||||
// Luke is the hero of Episode V.
|
// Luke is the hero of Episode V.
|
||||||
return self::luke();
|
return self::luke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Artoo is the hero otherwise.
|
// Artoo is the hero otherwise.
|
||||||
return self::artoo();
|
return self::artoo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param string $id
|
||||||
* @return mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
static function getHuman($id)
|
public static function getHuman($id)
|
||||||
{
|
{
|
||||||
$humans = self::humans();
|
$humans = self::humans();
|
||||||
return isset($humans[$id]) ? $humans[$id] : null;
|
|
||||||
|
return $humans[$id] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param string $id
|
||||||
* @return mixed|null
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
static function getDroid($id)
|
public static function getDroid($id)
|
||||||
{
|
{
|
||||||
$droids = self::droids();
|
$droids = self::droids();
|
||||||
return isset($droids[$id]) ? $droids[$id] : null;
|
|
||||||
|
return $droids[$id] ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests;
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
@ -14,7 +17,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingTheSchemaForTypes() : void
|
public function testAllowsQueryingTheSchemaForTypes() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionTypeQuery {
|
query IntrospectionTypeQuery {
|
||||||
__schema {
|
__schema {
|
||||||
types {
|
types {
|
||||||
@ -44,18 +47,26 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
['name' => '__EnumValue'],
|
['name' => '__EnumValue'],
|
||||||
['name' => '__Directive'],
|
['name' => '__Directive'],
|
||||||
['name' => '__DirectiveLocation'],
|
['name' => '__DirectiveLocation'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to test a query and the expected response.
|
||||||
|
*/
|
||||||
|
private function assertValidQuery($query, $expected) : void
|
||||||
|
{
|
||||||
|
$this->assertEquals(['data' => $expected], GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows querying the schema for query type')
|
* @see it('Allows querying the schema for query type')
|
||||||
*/
|
*/
|
||||||
public function testAllowsQueryingTheSchemaForQueryType() : void
|
public function testAllowsQueryingTheSchemaForQueryType() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionQueryTypeQuery {
|
query IntrospectionQueryTypeQuery {
|
||||||
__schema {
|
__schema {
|
||||||
queryType {
|
queryType {
|
||||||
@ -66,10 +77,8 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'__schema' => [
|
'__schema' => [
|
||||||
'queryType' => [
|
'queryType' => ['name' => 'Query'],
|
||||||
'name' => 'Query'
|
],
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -79,7 +88,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingTheSchemaForASpecificType() : void
|
public function testAllowsQueryingTheSchemaForASpecificType() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionDroidTypeQuery {
|
query IntrospectionDroidTypeQuery {
|
||||||
__type(name: "Droid") {
|
__type(name: "Droid") {
|
||||||
name
|
name
|
||||||
@ -87,9 +96,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => ['name' => 'Droid'],
|
||||||
'name' => 'Droid'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -99,7 +106,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingForAnObjectKind() : void
|
public function testAllowsQueryingForAnObjectKind() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionDroidKindQuery {
|
query IntrospectionDroidKindQuery {
|
||||||
__type(name: "Droid") {
|
__type(name: "Droid") {
|
||||||
name
|
name
|
||||||
@ -110,8 +117,8 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => [
|
||||||
'name' => 'Droid',
|
'name' => 'Droid',
|
||||||
'kind' => 'OBJECT'
|
'kind' => 'OBJECT',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -121,7 +128,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingForInterfaceKind() : void
|
public function testAllowsQueryingForInterfaceKind() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionCharacterKindQuery {
|
query IntrospectionCharacterKindQuery {
|
||||||
__type(name: "Character") {
|
__type(name: "Character") {
|
||||||
name
|
name
|
||||||
@ -132,8 +139,8 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => [
|
||||||
'name' => 'Character',
|
'name' => 'Character',
|
||||||
'kind' => 'INTERFACE'
|
'kind' => 'INTERFACE',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -143,7 +150,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingForObjectFields() : void
|
public function testAllowsQueryingForObjectFields() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionDroidFieldsQuery {
|
query IntrospectionDroidFieldsQuery {
|
||||||
__type(name: "Droid") {
|
__type(name: "Droid") {
|
||||||
name
|
name
|
||||||
@ -159,52 +166,52 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => [
|
||||||
'name' => 'Droid',
|
'name' => 'Droid',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
[
|
[
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'NON_NULL'
|
'kind' => 'NON_NULL',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR'
|
'kind' => 'SCALAR',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'friends',
|
'name' => 'friends',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'LIST'
|
'kind' => 'LIST',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'appearsIn',
|
'name' => 'appearsIn',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'LIST'
|
'kind' => 'LIST',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'secretBackstory',
|
'name' => 'secretBackstory',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR'
|
'kind' => 'SCALAR',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'primaryFunction',
|
'name' => 'primaryFunction',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR'
|
'kind' => 'SCALAR',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -214,7 +221,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingTheSchemaForNestedObjectFields() : void
|
public function testAllowsQueryingTheSchemaForNestedObjectFields() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionDroidNestedFieldsQuery {
|
query IntrospectionDroidNestedFieldsQuery {
|
||||||
__type(name: "Droid") {
|
__type(name: "Droid") {
|
||||||
name
|
name
|
||||||
@ -234,67 +241,67 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => [
|
||||||
'name' => 'Droid',
|
'name' => 'Droid',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
[
|
[
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'NON_NULL',
|
'kind' => 'NON_NULL',
|
||||||
'ofType' => [
|
'ofType' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR'
|
'kind' => 'SCALAR',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR',
|
'kind' => 'SCALAR',
|
||||||
'ofType' => null
|
'ofType' => null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'friends',
|
'name' => 'friends',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'LIST',
|
'kind' => 'LIST',
|
||||||
'ofType' => [
|
'ofType' => [
|
||||||
'name' => 'Character',
|
'name' => 'Character',
|
||||||
'kind' => 'INTERFACE'
|
'kind' => 'INTERFACE',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'appearsIn',
|
'name' => 'appearsIn',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => null,
|
'name' => null,
|
||||||
'kind' => 'LIST',
|
'kind' => 'LIST',
|
||||||
'ofType' => [
|
'ofType' => [
|
||||||
'name' => 'Episode',
|
'name' => 'Episode',
|
||||||
'kind' => 'ENUM'
|
'kind' => 'ENUM',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'secretBackstory',
|
'name' => 'secretBackstory',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR',
|
'kind' => 'SCALAR',
|
||||||
'ofType' => null
|
'ofType' => null,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'primaryFunction',
|
'name' => 'primaryFunction',
|
||||||
'type' => [
|
'type' => [
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
'kind' => 'SCALAR',
|
'kind' => 'SCALAR',
|
||||||
'ofType' => null
|
'ofType' => null,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -329,7 +336,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
|
|
||||||
$expected = array(
|
$expected = [
|
||||||
'__schema' => [
|
'__schema' => [
|
||||||
'queryType' => [
|
'queryType' => [
|
||||||
'fields' => [
|
'fields' => [
|
||||||
@ -337,13 +344,13 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
'name' => 'hero',
|
'name' => 'hero',
|
||||||
'args' => [
|
'args' => [
|
||||||
[
|
[
|
||||||
'defaultValue' => NULL,
|
'defaultValue' => null,
|
||||||
'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.',
|
'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.',
|
||||||
'name' => 'episode',
|
'name' => 'episode',
|
||||||
'type' => [
|
'type' => [
|
||||||
'kind' => 'ENUM',
|
'kind' => 'ENUM',
|
||||||
'name' => 'Episode',
|
'name' => 'Episode',
|
||||||
'ofType' => NULL,
|
'ofType' => null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -352,17 +359,17 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
'name' => 'human',
|
'name' => 'human',
|
||||||
'args' => [
|
'args' => [
|
||||||
[
|
[
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'description' => 'id of the human',
|
'description' => 'id of the human',
|
||||||
'type' => [
|
'type' => [
|
||||||
'kind' => 'NON_NULL',
|
'kind' => 'NON_NULL',
|
||||||
'name' => NULL,
|
'name' => null,
|
||||||
'ofType' => [
|
'ofType' => [
|
||||||
'kind' => 'SCALAR',
|
'kind' => 'SCALAR',
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'defaultValue' => NULL,
|
'defaultValue' => null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@ -370,25 +377,25 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
'name' => 'droid',
|
'name' => 'droid',
|
||||||
'args' => [
|
'args' => [
|
||||||
[
|
[
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'description' => 'id of the droid',
|
'description' => 'id of the droid',
|
||||||
'type' => [
|
'type' => [
|
||||||
'kind' => 'NON_NULL',
|
'kind' => 'NON_NULL',
|
||||||
'name' => NULL,
|
'name' => null,
|
||||||
'ofType' =>
|
'ofType' =>
|
||||||
[
|
[
|
||||||
'kind' => 'SCALAR',
|
'kind' => 'SCALAR',
|
||||||
'name' => 'String',
|
'name' => 'String',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'defaultValue' => NULL,
|
'defaultValue' => null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
];
|
||||||
|
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -398,7 +405,7 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsQueryingTheSchemaForDocumentation() : void
|
public function testAllowsQueryingTheSchemaForDocumentation() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query IntrospectionDroidDescriptionQuery {
|
query IntrospectionDroidDescriptionQuery {
|
||||||
__type(name: "Droid") {
|
__type(name: "Droid") {
|
||||||
name
|
name
|
||||||
@ -408,18 +415,10 @@ class StarWarsIntrospectionTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'__type' => [
|
'__type' => [
|
||||||
'name' => 'Droid',
|
'name' => 'Droid',
|
||||||
'description' => 'A mechanical creature in the Star Wars universe.'
|
'description' => 'A mechanical creature in the Star Wars universe.',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to test a query and the expected response.
|
|
||||||
*/
|
|
||||||
private function assertValidQuery($query, $expected)
|
|
||||||
{
|
|
||||||
$this->assertEquals(['data' => $expected], GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests;
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class StarWarsQueryTest extends TestCase
|
class StarWarsQueryTest extends TestCase
|
||||||
{
|
{
|
||||||
// Star Wars Query Tests
|
|
||||||
// Basic Queries
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Correctly identifies R2-D2 as the hero of the Star Wars Saga')
|
* @see it('Correctly identifies R2-D2 as the hero of the Star Wars Saga')
|
||||||
*/
|
*/
|
||||||
public function testCorrectlyIdentifiesR2D2AsTheHeroOfTheStarWarsSaga() : void
|
public function testCorrectlyIdentifiesR2D2AsTheHeroOfTheStarWarsSaga() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query HeroNameQuery {
|
query HeroNameQuery {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
@ -23,19 +22,30 @@ class StarWarsQueryTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'hero' => [
|
'hero' => ['name' => 'R2-D2'],
|
||||||
'name' => 'R2-D2'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to test a query and the expected response.
|
||||||
|
*/
|
||||||
|
private function assertValidQuery($query, $expected) : void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
['data' => $expected],
|
||||||
|
GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe: Nested Queries
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows us to query for the ID and friends of R2-D2')
|
* @see it('Allows us to query for the ID and friends of R2-D2')
|
||||||
*/
|
*/
|
||||||
public function testAllowsUsToQueryForTheIDAndFriendsOfR2D2() : void
|
public function testAllowsUsToQueryForTheIDAndFriendsOfR2D2() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query HeroNameAndFriendsQuery {
|
query HeroNameAndFriendsQuery {
|
||||||
hero {
|
hero {
|
||||||
id
|
id
|
||||||
@ -48,32 +58,26 @@ class StarWarsQueryTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'hero' => [
|
'hero' => [
|
||||||
'id' => '2001',
|
'id' => '2001',
|
||||||
'name' => 'R2-D2',
|
'name' => 'R2-D2',
|
||||||
'friends' => [
|
'friends' => [
|
||||||
[
|
['name' => 'Luke Skywalker'],
|
||||||
'name' => 'Luke Skywalker',
|
['name' => 'Han Solo'],
|
||||||
],
|
['name' => 'Leia Organa'],
|
||||||
[
|
],
|
||||||
'name' => 'Han Solo',
|
],
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'Leia Organa',
|
|
||||||
],
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: Nested Queries
|
// Describe: Using IDs and query parameters to refetch objects
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows us to query for the friends of friends of R2-D2')
|
* @see it('Allows us to query for the friends of friends of R2-D2')
|
||||||
*/
|
*/
|
||||||
public function testAllowsUsToQueryForTheFriendsOfFriendsOfR2D2() : void
|
public function testAllowsUsToQueryForTheFriendsOfFriendsOfR2D2() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query NestedQuery {
|
query NestedQuery {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
@ -89,52 +93,50 @@ class StarWarsQueryTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'hero' => [
|
'hero' => [
|
||||||
'name' => 'R2-D2',
|
'name' => 'R2-D2',
|
||||||
'friends' => [
|
'friends' => [
|
||||||
[
|
[
|
||||||
'name' => 'Luke Skywalker',
|
'name' => 'Luke Skywalker',
|
||||||
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI',],
|
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
['name' => 'Han Solo',],
|
['name' => 'Han Solo'],
|
||||||
['name' => 'Leia Organa',],
|
['name' => 'Leia Organa'],
|
||||||
['name' => 'C-3PO',],
|
['name' => 'C-3PO'],
|
||||||
['name' => 'R2-D2',],
|
['name' => 'R2-D2'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Han Solo',
|
'name' => 'Han Solo',
|
||||||
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'],
|
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
['name' => 'Luke Skywalker',],
|
['name' => 'Luke Skywalker'],
|
||||||
['name' => 'Leia Organa'],
|
['name' => 'Leia Organa'],
|
||||||
['name' => 'R2-D2',],
|
['name' => 'R2-D2'],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Leia Organa',
|
'name' => 'Leia Organa',
|
||||||
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'],
|
'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'],
|
||||||
'friends' =>
|
'friends' =>
|
||||||
[
|
[
|
||||||
['name' => 'Luke Skywalker',],
|
['name' => 'Luke Skywalker'],
|
||||||
['name' => 'Han Solo',],
|
['name' => 'Han Solo'],
|
||||||
['name' => 'C-3PO',],
|
['name' => 'C-3PO'],
|
||||||
['name' => 'R2-D2',],
|
['name' => 'R2-D2'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: Using IDs and query parameters to refetch objects
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Using IDs and query parameters to refetch objects')
|
* @see it('Using IDs and query parameters to refetch objects')
|
||||||
*/
|
*/
|
||||||
public function testAllowsUsToQueryForLukeSkywalkerDirectlyUsingHisID() : void
|
public function testAllowsUsToQueryForLukeSkywalkerDirectlyUsingHisID() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query FetchLukeQuery {
|
query FetchLukeQuery {
|
||||||
human(id: "1000") {
|
human(id: "1000") {
|
||||||
name
|
name
|
||||||
@ -142,9 +144,7 @@ class StarWarsQueryTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'human' => [
|
'human' => ['name' => 'Luke Skywalker'],
|
||||||
'name' => 'Luke Skywalker'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
@ -155,44 +155,49 @@ class StarWarsQueryTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testGenericQueryToGetLukeSkywalkerById() : void
|
public function testGenericQueryToGetLukeSkywalkerById() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query FetchSomeIDQuery($someId: String!) {
|
query FetchSomeIDQuery($someId: String!) {
|
||||||
human(id: $someId) {
|
human(id: $someId) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$params = [
|
$params = ['someId' => '1000'];
|
||||||
'someId' => '1000'
|
|
||||||
];
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'human' => [
|
'human' => ['name' => 'Luke Skywalker'],
|
||||||
'name' => 'Luke Skywalker'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertValidQueryWithParams($query, $params, $expected);
|
$this->assertValidQueryWithParams($query, $params, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to test a query with params and the expected response.
|
||||||
|
*/
|
||||||
|
private function assertValidQueryWithParams($query, $params, $expected)
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
['data' => $expected],
|
||||||
|
GraphQL::executeQuery(StarWarsSchema::build(), $query, null, null, $params)->toArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using aliases to change the key in the response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows us to create a generic query, then use it to fetch Han Solo using his ID')
|
* @see it('Allows us to create a generic query, then use it to fetch Han Solo using his ID')
|
||||||
*/
|
*/
|
||||||
public function testGenericQueryToGetHanSoloById() : void
|
public function testGenericQueryToGetHanSoloById() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query FetchSomeIDQuery($someId: String!) {
|
query FetchSomeIDQuery($someId: String!) {
|
||||||
human(id: $someId) {
|
human(id: $someId) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$params = [
|
$params = ['someId' => '1002'];
|
||||||
'someId' => '1002'
|
|
||||||
];
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'human' => [
|
'human' => ['name' => 'Han Solo'],
|
||||||
'name' => 'Han Solo'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQueryWithParams($query, $params, $expected);
|
$this->assertValidQueryWithParams($query, $params, $expected);
|
||||||
}
|
}
|
||||||
@ -202,30 +207,26 @@ class StarWarsQueryTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testGenericQueryWithInvalidId() : void
|
public function testGenericQueryWithInvalidId() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query humanQuery($id: String!) {
|
query humanQuery($id: String!) {
|
||||||
human(id: $id) {
|
human(id: $id) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$params = [
|
$params = ['id' => 'not a valid id'];
|
||||||
'id' => 'not a valid id'
|
$expected = ['human' => null];
|
||||||
];
|
|
||||||
$expected = [
|
|
||||||
'human' => null
|
|
||||||
];
|
|
||||||
$this->assertValidQueryWithParams($query, $params, $expected);
|
$this->assertValidQueryWithParams($query, $params, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using aliases to change the key in the response
|
// Uses fragments to express more complex queries
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows us to query for Luke, changing his key with an alias')
|
* @see it('Allows us to query for Luke, changing his key with an alias')
|
||||||
*/
|
*/
|
||||||
function testLukeKeyAlias()
|
public function testLukeKeyAlias() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query FetchLukeAliased {
|
query FetchLukeAliased {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
@ -233,9 +234,7 @@ class StarWarsQueryTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'luke' => [
|
'luke' => ['name' => 'Luke Skywalker'],
|
||||||
'name' => 'Luke Skywalker'
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -243,9 +242,9 @@ class StarWarsQueryTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @see it('Allows us to query for both Luke and Leia, using two root fields and an alias')
|
* @see it('Allows us to query for both Luke and Leia, using two root fields and an alias')
|
||||||
*/
|
*/
|
||||||
function testTwoRootKeysAsAnAlias()
|
public function testTwoRootKeysAsAnAlias() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query FetchLukeAndLeiaAliased {
|
query FetchLukeAndLeiaAliased {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
@ -256,24 +255,18 @@ class StarWarsQueryTest extends TestCase
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'luke' => [
|
'luke' => ['name' => 'Luke Skywalker'],
|
||||||
'name' => 'Luke Skywalker'
|
'leia' => ['name' => 'Leia Organa'],
|
||||||
],
|
|
||||||
'leia' => [
|
|
||||||
'name' => 'Leia Organa'
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses fragments to express more complex queries
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Allows us to query using duplicated content')
|
* @see it('Allows us to query using duplicated content')
|
||||||
*/
|
*/
|
||||||
function testQueryUsingDuplicatedContent()
|
public function testQueryUsingDuplicatedContent() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query DuplicateFields {
|
query DuplicateFields {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
@ -287,13 +280,13 @@ class StarWarsQueryTest extends TestCase
|
|||||||
';
|
';
|
||||||
$expected = [
|
$expected = [
|
||||||
'luke' => [
|
'luke' => [
|
||||||
'name' => 'Luke Skywalker',
|
'name' => 'Luke Skywalker',
|
||||||
'homePlanet' => 'Tatooine'
|
'homePlanet' => 'Tatooine',
|
||||||
],
|
],
|
||||||
'leia' => [
|
'leia' => [
|
||||||
'name' => 'Leia Organa',
|
'name' => 'Leia Organa',
|
||||||
'homePlanet' => 'Alderaan'
|
'homePlanet' => 'Alderaan',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -301,7 +294,7 @@ class StarWarsQueryTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @see it('Allows us to use a fragment to avoid duplicating content')
|
* @see it('Allows us to use a fragment to avoid duplicating content')
|
||||||
*/
|
*/
|
||||||
function testUsingFragment()
|
public function testUsingFragment() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query UseFragment {
|
query UseFragment {
|
||||||
@ -321,13 +314,13 @@ class StarWarsQueryTest extends TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'luke' => [
|
'luke' => [
|
||||||
'name' => 'Luke Skywalker',
|
'name' => 'Luke Skywalker',
|
||||||
'homePlanet' => 'Tatooine'
|
'homePlanet' => 'Tatooine',
|
||||||
],
|
],
|
||||||
'leia' => [
|
'leia' => [
|
||||||
'name' => 'Leia Organa',
|
'name' => 'Leia Organa',
|
||||||
'homePlanet' => 'Alderaan'
|
'homePlanet' => 'Alderaan',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
@ -337,7 +330,7 @@ class StarWarsQueryTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testVerifyThatR2D2IsADroid() : void
|
public function testVerifyThatR2D2IsADroid() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query CheckTypeOfR2 {
|
query CheckTypeOfR2 {
|
||||||
hero {
|
hero {
|
||||||
__typename
|
__typename
|
||||||
@ -348,7 +341,7 @@ class StarWarsQueryTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'hero' => [
|
'hero' => [
|
||||||
'__typename' => 'Droid',
|
'__typename' => 'Droid',
|
||||||
'name' => 'R2-D2'
|
'name' => 'R2-D2',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
@ -371,32 +364,10 @@ class StarWarsQueryTest extends TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'hero' => [
|
'hero' => [
|
||||||
'__typename' => 'Human',
|
'__typename' => 'Human',
|
||||||
'name' => 'Luke Skywalker'
|
'name' => 'Luke Skywalker',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertValidQuery($query, $expected);
|
$this->assertValidQuery($query, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to test a query and the expected response.
|
|
||||||
*/
|
|
||||||
private function assertValidQuery($query, $expected)
|
|
||||||
{
|
|
||||||
$this->assertEquals(
|
|
||||||
['data' => $expected],
|
|
||||||
GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to test a query with params and the expected response.
|
|
||||||
*/
|
|
||||||
private function assertValidQueryWithParams($query, $params, $expected)
|
|
||||||
{
|
|
||||||
$this->assertEquals(
|
|
||||||
['data' => $expected],
|
|
||||||
GraphQL::executeQuery(StarWarsSchema::build(), $query, null, null, $params)->toArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests;
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,13 +14,16 @@ namespace GraphQL\Tests;
|
|||||||
* NOTE: This may contain spoilers for the original Star
|
* NOTE: This may contain spoilers for the original Star
|
||||||
* Wars trilogy.
|
* Wars trilogy.
|
||||||
*/
|
*/
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
use GraphQL\Type\Definition\NonNull;
|
use GraphQL\Type\Definition\NonNull;
|
||||||
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 function array_intersect_key;
|
||||||
|
use function array_map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using our shorthand to describe type systems, the type system for our
|
* Using our shorthand to describe type systems, the type system for our
|
||||||
@ -56,10 +62,9 @@ use GraphQL\Type\Definition\Type;
|
|||||||
*
|
*
|
||||||
* We begin by setting up our schema.
|
* We begin by setting up our schema.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class StarWarsSchema
|
class StarWarsSchema
|
||||||
{
|
{
|
||||||
public static function build()
|
public static function build() : Schema
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The original trilogy consists of three movies.
|
* The original trilogy consists of three movies.
|
||||||
@ -68,22 +73,22 @@ class StarWarsSchema
|
|||||||
* enum Episode { NEWHOPE, EMPIRE, JEDI }
|
* enum Episode { NEWHOPE, EMPIRE, JEDI }
|
||||||
*/
|
*/
|
||||||
$episodeEnum = new EnumType([
|
$episodeEnum = new EnumType([
|
||||||
'name' => 'Episode',
|
'name' => 'Episode',
|
||||||
'description' => 'One of the films in the Star Wars Trilogy',
|
'description' => 'One of the films in the Star Wars Trilogy',
|
||||||
'values' => [
|
'values' => [
|
||||||
'NEWHOPE' => [
|
'NEWHOPE' => [
|
||||||
'value' => 4,
|
'value' => 4,
|
||||||
'description' => 'Released in 1977.'
|
'description' => 'Released in 1977.',
|
||||||
],
|
],
|
||||||
'EMPIRE' => [
|
'EMPIRE' => [
|
||||||
'value' => 5,
|
'value' => 5,
|
||||||
'description' => 'Released in 1980.'
|
'description' => 'Released in 1980.',
|
||||||
],
|
],
|
||||||
'JEDI' => [
|
'JEDI' => [
|
||||||
'value' => 6,
|
'value' => 6,
|
||||||
'description' => 'Released in 1983.'
|
'description' => 'Released in 1983.',
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$humanType = null;
|
$humanType = null;
|
||||||
@ -101,28 +106,28 @@ class StarWarsSchema
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
$characterInterface = new InterfaceType([
|
$characterInterface = new InterfaceType([
|
||||||
'name' => 'Character',
|
'name' => 'Character',
|
||||||
'description' => 'A character in the Star Wars Trilogy',
|
'description' => 'A character in the Star Wars Trilogy',
|
||||||
'fields' => function() use (&$characterInterface, $episodeEnum) {
|
'fields' => function () use (&$characterInterface, $episodeEnum) {
|
||||||
return [
|
return [
|
||||||
'id' => [
|
'id' => [
|
||||||
'type' => Type::nonNull(Type::string()),
|
'type' => Type::nonNull(Type::string()),
|
||||||
'description' => 'The id of the character.',
|
'description' => 'The id of the character.',
|
||||||
],
|
],
|
||||||
'name' => [
|
'name' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'The name of the character.'
|
'description' => 'The name of the character.',
|
||||||
],
|
],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
'type' => Type::listOf($characterInterface),
|
'type' => Type::listOf($characterInterface),
|
||||||
'description' => 'The friends of the character, or an empty list if they have none.',
|
'description' => 'The friends of the character, or an empty list if they have none.',
|
||||||
],
|
],
|
||||||
'appearsIn' => [
|
'appearsIn' => [
|
||||||
'type' => Type::listOf($episodeEnum),
|
'type' => Type::listOf($episodeEnum),
|
||||||
'description' => 'Which movies they appear in.'
|
'description' => 'Which movies they appear in.',
|
||||||
],
|
],
|
||||||
'secretBackstory' => [
|
'secretBackstory' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'All secrets about their past.',
|
'description' => 'All secrets about their past.',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
@ -145,26 +150,26 @@ class StarWarsSchema
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
$humanType = new ObjectType([
|
$humanType = new ObjectType([
|
||||||
'name' => 'Human',
|
'name' => 'Human',
|
||||||
'description' => 'A humanoid creature in the Star Wars universe.',
|
'description' => 'A humanoid creature in the Star Wars universe.',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => [
|
'id' => [
|
||||||
'type' => new NonNull(Type::string()),
|
'type' => new NonNull(Type::string()),
|
||||||
'description' => 'The id of the human.',
|
'description' => 'The id of the human.',
|
||||||
],
|
],
|
||||||
'name' => [
|
'name' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'The name of the human.',
|
'description' => 'The name of the human.',
|
||||||
],
|
],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
'type' => Type::listOf($characterInterface),
|
'type' => Type::listOf($characterInterface),
|
||||||
'description' => 'The friends of the human, or an empty list if they have none.',
|
'description' => 'The friends of the human, or an empty list if they have none.',
|
||||||
'resolve' => function ($human, $args, $context, ResolveInfo $info) {
|
'resolve' => function ($human, $args, $context, ResolveInfo $info) {
|
||||||
$fieldSelection = $info->getFieldSelection();
|
$fieldSelection = $info->getFieldSelection();
|
||||||
$fieldSelection['id'] = true;
|
$fieldSelection['id'] = true;
|
||||||
|
|
||||||
$friends = array_map(
|
$friends = array_map(
|
||||||
function($friend) use ($fieldSelection) {
|
function ($friend) use ($fieldSelection) {
|
||||||
return array_intersect_key($friend, $fieldSelection);
|
return array_intersect_key($friend, $fieldSelection);
|
||||||
},
|
},
|
||||||
StarWarsData::getFriends($human)
|
StarWarsData::getFriends($human)
|
||||||
@ -173,24 +178,24 @@ class StarWarsSchema
|
|||||||
return $friends;
|
return $friends;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'appearsIn' => [
|
'appearsIn' => [
|
||||||
'type' => Type::listOf($episodeEnum),
|
'type' => Type::listOf($episodeEnum),
|
||||||
'description' => 'Which movies they appear in.'
|
'description' => 'Which movies they appear in.',
|
||||||
],
|
],
|
||||||
'homePlanet' => [
|
'homePlanet' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'The home planet of the human, or null if unknown.'
|
'description' => 'The home planet of the human, or null if unknown.',
|
||||||
],
|
],
|
||||||
'secretBackstory' => [
|
'secretBackstory' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'Where are they from and how they came to be who they are.',
|
'description' => 'Where are they from and how they came to be who they are.',
|
||||||
'resolve' => function() {
|
'resolve' => function () {
|
||||||
// This is to demonstrate error reporting
|
// This is to demonstrate error reporting
|
||||||
throw new \Exception('secretBackstory is secret.');
|
throw new \Exception('secretBackstory is secret.');
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'interfaces' => [$characterInterface]
|
'interfaces' => [$characterInterface],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,42 +212,42 @@ class StarWarsSchema
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
$droidType = new ObjectType([
|
$droidType = new ObjectType([
|
||||||
'name' => 'Droid',
|
'name' => 'Droid',
|
||||||
'description' => 'A mechanical creature in the Star Wars universe.',
|
'description' => 'A mechanical creature in the Star Wars universe.',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => [
|
'id' => [
|
||||||
'type' => Type::nonNull(Type::string()),
|
'type' => Type::nonNull(Type::string()),
|
||||||
'description' => 'The id of the droid.',
|
'description' => 'The id of the droid.',
|
||||||
],
|
],
|
||||||
'name' => [
|
'name' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'The name of the droid.'
|
'description' => 'The name of the droid.',
|
||||||
],
|
],
|
||||||
'friends' => [
|
'friends' => [
|
||||||
'type' => Type::listOf($characterInterface),
|
'type' => Type::listOf($characterInterface),
|
||||||
'description' => 'The friends of the droid, or an empty list if they have none.',
|
'description' => 'The friends of the droid, or an empty list if they have none.',
|
||||||
'resolve' => function ($droid) {
|
'resolve' => function ($droid) {
|
||||||
return StarWarsData::getFriends($droid);
|
return StarWarsData::getFriends($droid);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'appearsIn' => [
|
'appearsIn' => [
|
||||||
'type' => Type::listOf($episodeEnum),
|
'type' => Type::listOf($episodeEnum),
|
||||||
'description' => 'Which movies they appear in.'
|
'description' => 'Which movies they appear in.',
|
||||||
],
|
],
|
||||||
'secretBackstory' => [
|
'secretBackstory' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'Construction date and the name of the designer.',
|
'description' => 'Construction date and the name of the designer.',
|
||||||
'resolve' => function() {
|
'resolve' => function () {
|
||||||
// This is to demonstrate error reporting
|
// This is to demonstrate error reporting
|
||||||
throw new \Exception('secretBackstory is secret.');
|
throw new \Exception('secretBackstory is secret.');
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'primaryFunction' => [
|
'primaryFunction' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'description' => 'The primary function of the droid.'
|
'description' => 'The primary function of the droid.',
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'interfaces' => [$characterInterface]
|
'interfaces' => [$characterInterface],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,49 +265,51 @@ class StarWarsSchema
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
$queryType = new ObjectType([
|
$queryType = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'hero' => [
|
'hero' => [
|
||||||
'type' => $characterInterface,
|
'type' => $characterInterface,
|
||||||
'args' => [
|
'args' => [
|
||||||
'episode' => [
|
'episode' => [
|
||||||
'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.',
|
'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.',
|
||||||
'type' => $episodeEnum
|
'type' => $episodeEnum,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'resolve' => function ($root, $args) {
|
'resolve' => function ($root, $args) {
|
||||||
return StarWarsData::getHero(isset($args['episode']) ? $args['episode'] : null);
|
return StarWarsData::getHero($args['episode'] ?? null);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'human' => [
|
'human' => [
|
||||||
'type' => $humanType,
|
'type' => $humanType,
|
||||||
'args' => [
|
'args' => [
|
||||||
'id' => [
|
'id' => [
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'description' => 'id of the human',
|
'description' => 'id of the human',
|
||||||
'type' => Type::nonNull(Type::string())
|
'type' => Type::nonNull(Type::string()),
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'resolve' => function ($root, $args) {
|
'resolve' => function ($root, $args) {
|
||||||
$humans = StarWarsData::humans();
|
$humans = StarWarsData::humans();
|
||||||
return isset($humans[$args['id']]) ? $humans[$args['id']] : null;
|
|
||||||
}
|
return $humans[$args['id']] ?? null;
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'droid' => [
|
'droid' => [
|
||||||
'type' => $droidType,
|
'type' => $droidType,
|
||||||
'args' => [
|
'args' => [
|
||||||
'id' => [
|
'id' => [
|
||||||
'name' => 'id',
|
'name' => 'id',
|
||||||
'description' => 'id of the droid',
|
'description' => 'id of the droid',
|
||||||
'type' => Type::nonNull(Type::string())
|
'type' => Type::nonNull(Type::string()),
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'resolve' => function ($root, $args) {
|
'resolve' => function ($root, $args) {
|
||||||
$droids = StarWarsData::droids();
|
$droids = StarWarsData::droids();
|
||||||
return isset($droids[$args['id']]) ? $droids[$args['id']] : null;
|
|
||||||
}
|
return $droids[$args['id']] ?? null;
|
||||||
]
|
},
|
||||||
]
|
],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return new Schema(['query' => $queryType]);
|
return new Schema(['query' => $queryType]);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests;
|
namespace GraphQL\Tests;
|
||||||
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
@ -15,7 +18,7 @@ class StarWarsValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testValidatesAComplexButValidQuery() : void
|
public function testValidatesAComplexButValidQuery() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query NestedQueryWithFragment {
|
query NestedQueryWithFragment {
|
||||||
hero {
|
hero {
|
||||||
...NameAndAppearances
|
...NameAndAppearances
|
||||||
@ -37,12 +40,22 @@ class StarWarsValidationTest extends TestCase
|
|||||||
$this->assertEquals(true, empty($errors));
|
$this->assertEquals(true, empty($errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to test a query and the expected response.
|
||||||
|
*/
|
||||||
|
private function validationErrors($query)
|
||||||
|
{
|
||||||
|
$ast = Parser::parse($query);
|
||||||
|
|
||||||
|
return DocumentValidator::validate(StarWarsSchema::build(), $ast);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Notes that non-existent fields are invalid')
|
* @see it('Notes that non-existent fields are invalid')
|
||||||
*/
|
*/
|
||||||
public function testThatNonExistentFieldsAreInvalid() : void
|
public function testThatNonExistentFieldsAreInvalid() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query HeroSpaceshipQuery {
|
query HeroSpaceshipQuery {
|
||||||
hero {
|
hero {
|
||||||
favoriteSpaceship
|
favoriteSpaceship
|
||||||
@ -73,7 +86,7 @@ class StarWarsValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDisallowsFieldsOnScalars() : void
|
public function testDisallowsFieldsOnScalars() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query HeroFieldsOnScalarQuery {
|
query HeroFieldsOnScalarQuery {
|
||||||
hero {
|
hero {
|
||||||
name {
|
name {
|
||||||
@ -91,7 +104,7 @@ class StarWarsValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDisallowsObjectFieldsOnInterfaces() : void
|
public function testDisallowsObjectFieldsOnInterfaces() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query DroidFieldOnCharacter {
|
query DroidFieldOnCharacter {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
@ -108,7 +121,7 @@ class StarWarsValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsObjectFieldsInFragments() : void
|
public function testAllowsObjectFieldsInFragments() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query DroidFieldInFragment {
|
query DroidFieldInFragment {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
@ -129,7 +142,7 @@ class StarWarsValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testAllowsObjectFieldsInInlineFragments() : void
|
public function testAllowsObjectFieldsInInlineFragments() : void
|
||||||
{
|
{
|
||||||
$query = '
|
$query = '
|
||||||
query DroidFieldInFragment {
|
query DroidFieldInFragment {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
@ -142,13 +155,4 @@ class StarWarsValidationTest extends TestCase
|
|||||||
$errors = $this->validationErrors($query);
|
$errors = $this->validationErrors($query);
|
||||||
$this->assertEquals(true, empty($errors));
|
$this->assertEquals(true, empty($errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to test a query and the expected response.
|
|
||||||
*/
|
|
||||||
private function validationErrors($query)
|
|
||||||
{
|
|
||||||
$ast = Parser::parse($query);
|
|
||||||
return DocumentValidator::validate(StarWarsSchema::build(), $ast);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,68 +1,77 @@
|
|||||||
<?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()
|
||||||
{
|
{
|
||||||
$ColorType = new EnumType([
|
$ColorType = new EnumType([
|
||||||
'name' => 'Color',
|
'name' => 'Color',
|
||||||
'values' => [
|
'values' => [
|
||||||
'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([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'colorEnum' => [
|
'colorEnum' => [
|
||||||
'type' => $ColorType,
|
'type' => $ColorType,
|
||||||
'args' => [
|
'args' => [
|
||||||
'fromEnum' => ['type' => $ColorType],
|
'fromEnum' => ['type' => $ColorType],
|
||||||
'fromInt' => ['type' => Type::int()],
|
'fromInt' => ['type' => Type::int()],
|
||||||
'fromString' => ['type' => Type::string()],
|
'fromString' => ['type' => Type::string()],
|
||||||
],
|
],
|
||||||
'resolve' => function ($value, $args) {
|
'resolve' => function ($value, $args) {
|
||||||
@ -75,28 +84,28 @@ 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(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'fromEnum' => ['type' => $ColorType],
|
'fromEnum' => ['type' => $ColorType],
|
||||||
'fromInt' => ['type' => Type::int()],
|
'fromInt' => ['type' => Type::int()],
|
||||||
],
|
],
|
||||||
'resolve' => function ($value, $args) {
|
'resolve' => function ($value, $args) {
|
||||||
if (isset($args['fromInt'])) {
|
if (isset($args['fromInt'])) {
|
||||||
@ -105,75 +114,76 @@ class EnumTypeTest extends TestCase
|
|||||||
if (isset($args['fromEnum'])) {
|
if (isset($args['fromEnum'])) {
|
||||||
return $args['fromEnum'];
|
return $args['fromEnum'];
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
'complexEnum' => [
|
'complexEnum' => [
|
||||||
'type' => $ComplexEnum,
|
'type' => $ComplexEnum,
|
||||||
'args' => [
|
'args' => [
|
||||||
'fromEnum' => [
|
'fromEnum' => [
|
||||||
'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) {
|
'resolve' => function ($value, $args) use ($Complex2) {
|
||||||
if (!empty($args['provideGoodValue'])) {
|
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([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'favoriteEnum' => [
|
'favoriteEnum' => [
|
||||||
'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([
|
||||||
'name' => 'Subscription',
|
'name' => 'Subscription',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'subscribeToEnum' => [
|
'subscribeToEnum' => [
|
||||||
'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;
|
||||||
$this->Complex2 = $Complex2;
|
$this->Complex2 = $Complex2;
|
||||||
$this->ComplexEnum = $ComplexEnum;
|
$this->ComplexEnum = $ComplexEnum;
|
||||||
|
|
||||||
$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)],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -266,9 +298,9 @@ class EnumTypeTest extends TestCase
|
|||||||
'{ colorEnum(fromString: "GREEN") }',
|
'{ colorEnum(fromString: "GREEN") }',
|
||||||
null,
|
null,
|
||||||
[
|
[
|
||||||
'message' => 'Expected a value of type "Color" but received: GREEN',
|
'message' => 'Expected a value of type "Color" but received: GREEN',
|
||||||
'locations' => [new SourceLocation(1, 3)],
|
'locations' => [new SourceLocation(1, 3)],
|
||||||
'path' => ['colorEnum'],
|
'path' => ['colorEnum'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -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()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +457,7 @@ class EnumTypeTest extends TestCase
|
|||||||
public function testPresentsGetValuesAPIForComplexEnums() : void
|
public function testPresentsGetValuesAPIForComplexEnums() : void
|
||||||
{
|
{
|
||||||
$ComplexEnum = $this->ComplexEnum;
|
$ComplexEnum = $this->ComplexEnum;
|
||||||
$values = $ComplexEnum->getValues();
|
$values = $ComplexEnum->getValues();
|
||||||
|
|
||||||
$this->assertEquals(2, count($values));
|
$this->assertEquals(2, count($values));
|
||||||
$this->assertEquals('ONE', $values[0]->name);
|
$this->assertEquals('ONE', $values[0]->name);
|
||||||
@ -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);
|
||||||
@ -489,35 +531,14 @@ class EnumTypeTest extends TestCase
|
|||||||
|
|
||||||
$this->assertArraySubset(
|
$this->assertArraySubset(
|
||||||
[
|
[
|
||||||
'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
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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,124 +13,84 @@ 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()
|
||||||
{
|
{
|
||||||
$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,143 +98,166 @@ 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([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'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([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'postStory' => [
|
'postStory' => [
|
||||||
'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([
|
||||||
'name' => 'PostCommentMutationInput',
|
'name' => 'PostCommentMutationInput',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'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(),
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,29 +265,29 @@ class ResolutionTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Has internal types by default:
|
// Has internal types by default:
|
||||||
$eagerTypeResolution = new EagerResolution([]);
|
$eagerTypeResolution = new EagerResolution([]);
|
||||||
$expectedTypeMap = [
|
$expectedTypeMap = [
|
||||||
'ID' => Type::id(),
|
'ID' => Type::id(),
|
||||||
'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());
|
||||||
|
|
||||||
$expectedDescriptor = [
|
$expectedDescriptor = [
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'typeMap' => [
|
'typeMap' => [
|
||||||
'ID' => 1,
|
'ID' => 1,
|
||||||
'String' => 1,
|
'String' => 1,
|
||||||
'Float' => 1,
|
'Float' => 1,
|
||||||
'Int' => 1,
|
'Int' => 1,
|
||||||
'Boolean' => 1,
|
'Boolean' => 1,
|
||||||
],
|
],
|
||||||
'possibleTypeMap' => []
|
'possibleTypeMap' => [],
|
||||||
];
|
];
|
||||||
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
|
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
|
||||||
|
|
||||||
$this->assertSame(null, $eagerTypeResolution->resolveType('User'));
|
$this->assertNull($eagerTypeResolution->resolveType('User'));
|
||||||
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->node));
|
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->node));
|
||||||
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->content));
|
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->content));
|
||||||
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->mention));
|
$this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->mention));
|
||||||
@ -321,72 +307,76 @@ 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 = [
|
||||||
'Query' => $this->query,
|
'Query' => $this->query,
|
||||||
'Mutation' => $this->mutation,
|
'Mutation' => $this->mutation,
|
||||||
'User' => $this->user,
|
'User' => $this->user,
|
||||||
'Node' => $this->node,
|
'Node' => $this->node,
|
||||||
'String' => Type::string(),
|
'String' => Type::string(),
|
||||||
'Content' => $this->content,
|
'Content' => $this->content,
|
||||||
'Comment' => $this->comment,
|
'Comment' => $this->comment,
|
||||||
'Mention' => $this->mention,
|
'Mention' => $this->mention,
|
||||||
'BlogStory' => $this->blogStory,
|
'BlogStory' => $this->blogStory,
|
||||||
'Category' => $this->category,
|
'Category' => $this->category,
|
||||||
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
||||||
'ID' => Type::id(),
|
'ID' => Type::id(),
|
||||||
'PostStoryMutation' => $this->postStoryMutation,
|
'PostStoryMutation' => $this->postStoryMutation,
|
||||||
'PostCommentMutationInput' => $this->postCommentMutationInput,
|
'PostCommentMutationInput' => $this->postCommentMutationInput,
|
||||||
'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());
|
||||||
|
|
||||||
$expectedDescriptor = [
|
$expectedDescriptor = [
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'typeMap' => [
|
'typeMap' => [
|
||||||
'Query' => 1,
|
'Query' => 1,
|
||||||
'Mutation' => 1,
|
'Mutation' => 1,
|
||||||
'User' => 1,
|
'User' => 1,
|
||||||
'Node' => 1,
|
'Node' => 1,
|
||||||
'String' => 1,
|
'String' => 1,
|
||||||
'Content' => 1,
|
'Content' => 1,
|
||||||
'Comment' => 1,
|
'Comment' => 1,
|
||||||
'Mention' => 1,
|
'Mention' => 1,
|
||||||
'BlogStory' => 1,
|
'BlogStory' => 1,
|
||||||
'Category' => 1,
|
'Category' => 1,
|
||||||
'PostStoryMutationInput' => 1,
|
'PostStoryMutationInput' => 1,
|
||||||
'ID' => 1,
|
'ID' => 1,
|
||||||
'PostStoryMutation' => 1,
|
'PostStoryMutation' => 1,
|
||||||
'PostCommentMutationInput' => 1,
|
'PostCommentMutationInput' => 1,
|
||||||
'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,52 +403,53 @@ 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 = [
|
||||||
'Video' => $this->video,
|
'Video' => $this->video,
|
||||||
'Node' => $this->node,
|
'Node' => $this->node,
|
||||||
'String' => Type::string(),
|
'String' => Type::string(),
|
||||||
'Content' => $this->content,
|
'Content' => $this->content,
|
||||||
'User' => $this->user,
|
'User' => $this->user,
|
||||||
'Comment' => $this->comment,
|
'Comment' => $this->comment,
|
||||||
'Category' => $this->category,
|
'Category' => $this->category,
|
||||||
'VideoMetadata' => $this->videoMetadata,
|
'VideoMetadata' => $this->videoMetadata,
|
||||||
'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());
|
||||||
|
|
||||||
$expectedDescriptor = [
|
$expectedDescriptor = [
|
||||||
'version' => '1.0',
|
'version' => '1.0',
|
||||||
'typeMap' => [
|
'typeMap' => [
|
||||||
'Video' => 1,
|
'Video' => 1,
|
||||||
'Node' => 1,
|
'Node' => 1,
|
||||||
'String' => 1,
|
'String' => 1,
|
||||||
'Content' => 1,
|
'Content' => 1,
|
||||||
'User' => 1,
|
'User' => 1,
|
||||||
'Comment' => 1,
|
'Comment' => 1,
|
||||||
'Category' => 1,
|
'Category' => 1,
|
||||||
'VideoMetadata' => 1,
|
'VideoMetadata' => 1,
|
||||||
'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' => [
|
'Content' => ['Video' => 1],
|
||||||
'Video' => 1
|
],
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
|
$this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor());
|
||||||
}
|
}
|
||||||
@ -463,11 +457,11 @@ class ResolutionTest extends TestCase
|
|||||||
public function testLazyResolutionFollowsEagerResolution() : void
|
public function testLazyResolutionFollowsEagerResolution() : void
|
||||||
{
|
{
|
||||||
// Lazy resolution should work the same way as eager resolution works, except that it should load types on demand
|
// Lazy resolution should work the same way as eager resolution works, except that it should load types on demand
|
||||||
$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);
|
||||||
@ -478,11 +472,12 @@ class ResolutionTest extends TestCase
|
|||||||
|
|
||||||
$eager = new EagerResolution([$this->query, $this->mutation]);
|
$eager = new EagerResolution([$this->query, $this->mutation]);
|
||||||
|
|
||||||
$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));
|
||||||
@ -515,8 +513,8 @@ class ResolutionTest extends TestCase
|
|||||||
$this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention));
|
$this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention));
|
||||||
|
|
||||||
$called = 0;
|
$called = 0;
|
||||||
$eager = new EagerResolution([$this->video]);
|
$eager = new EagerResolution([$this->video]);
|
||||||
$lazy = new LazyResolution($eager->getDescriptor(), $typeLoader);
|
$lazy = new LazyResolution($eager->getDescriptor(), $typeLoader);
|
||||||
|
|
||||||
$this->assertEquals($eager->resolveType('VideoMetadata'), $lazy->resolveType('VideoMetadata'));
|
$this->assertEquals($eager->resolveType('VideoMetadata'), $lazy->resolveType('VideoMetadata'));
|
||||||
$this->assertEquals($eager->resolveType('Video'), $lazy->resolveType('Video'));
|
$this->assertEquals($eager->resolveType('Video'), $lazy->resolveType('Video'));
|
||||||
@ -527,40 +525,6 @@ 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(){
|
|
||||||
|
|
||||||
$descriptor = [
|
|
||||||
'version' => '1.0',
|
|
||||||
'typeMap' => [
|
|
||||||
'null' => 1,
|
|
||||||
'int' => 1
|
|
||||||
],
|
|
||||||
'possibleTypeMap' => [
|
|
||||||
'a' => [
|
|
||||||
'null' => 1,
|
|
||||||
],
|
|
||||||
'b' => [
|
|
||||||
'int' => 1
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
$invalidTypeLoader = function($name) {
|
|
||||||
switch ($name) {
|
|
||||||
case 'null':
|
|
||||||
return null;
|
|
||||||
case 'int':
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$lazy = new LazyResolution($descriptor, $invalidTypeLoader);
|
|
||||||
$value = $lazy->resolveType('null');
|
|
||||||
$this->assertEquals(null, $value);
|
|
||||||
|
|
||||||
return $lazy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testLazyThrowsOnInvalidLoadedType() : void
|
public function testLazyThrowsOnInvalidLoadedType() : void
|
||||||
{
|
{
|
||||||
$lazy = $this->createLazy();
|
$lazy = $this->createLazy();
|
||||||
@ -569,9 +533,39 @@ class ResolutionTest extends TestCase
|
|||||||
$lazy->resolveType('int');
|
$lazy->resolveType('int');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createLazy()
|
||||||
|
{
|
||||||
|
$descriptor = [
|
||||||
|
'version' => '1.0',
|
||||||
|
'typeMap' => [
|
||||||
|
'null' => 1,
|
||||||
|
'int' => 1,
|
||||||
|
],
|
||||||
|
'possibleTypeMap' => [
|
||||||
|
'a' => ['null' => 1],
|
||||||
|
'b' => ['int' => 1],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$invalidTypeLoader = function ($name) {
|
||||||
|
switch ($name) {
|
||||||
|
case 'null':
|
||||||
|
return null;
|
||||||
|
case 'int':
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$lazy = new LazyResolution($descriptor, $invalidTypeLoader);
|
||||||
|
$value = $lazy->resolveType('null');
|
||||||
|
$this->assertEquals(null, $value);
|
||||||
|
|
||||||
|
return $lazy;
|
||||||
|
}
|
||||||
|
|
||||||
public function testLazyThrowsOnInvalidLoadedPossibleType() : void
|
public function testLazyThrowsOnInvalidLoadedPossibleType() : void
|
||||||
{
|
{
|
||||||
$tmp = new InterfaceType(['name' => 'a', 'fields' => []]);
|
$tmp = new InterfaceType(['name' => 'a', 'fields' => []]);
|
||||||
$lazy = $this->createLazy();
|
$lazy = $this->createLazy();
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
$this->expectExceptionMessage('Lazy Type Resolution Error: Implementation null of interface a is expected to be instance of ObjectType, but got NULL');
|
$this->expectExceptionMessage('Lazy Type Resolution Error: Implementation null of interface a is expected to be instance of ObjectType, but got NULL');
|
||||||
@ -580,7 +574,7 @@ class ResolutionTest extends TestCase
|
|||||||
|
|
||||||
public function testLazyThrowsOnInvalidLoadedPossibleTypeWithInteger() : void
|
public function testLazyThrowsOnInvalidLoadedPossibleTypeWithInteger() : void
|
||||||
{
|
{
|
||||||
$tmp = new InterfaceType(['name' => 'b', 'fields' => []]);
|
$tmp = new InterfaceType(['name' => 'b', 'fields' => []]);
|
||||||
$lazy = $this->createLazy();
|
$lazy = $this->createLazy();
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
$this->expectExceptionMessage('Lazy Type Resolution Error: Expecting GraphQL Type instance, but got integer');
|
$this->expectExceptionMessage('Lazy Type Resolution Error: Expecting GraphQL Type instance, but got integer');
|
||||||
|
@ -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
|
||||||
@ -13,53 +16,56 @@ class ResolveInfoTest extends TestCase
|
|||||||
public function testFieldSelection() : void
|
public function testFieldSelection() : void
|
||||||
{
|
{
|
||||||
$image = new ObjectType([
|
$image = new ObjectType([
|
||||||
'name' => 'Image',
|
'name' => 'Image',
|
||||||
'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' => [
|
||||||
'width' => ['type' => Type::int()],
|
'type' => $image,
|
||||||
'height' => ['type' => Type::int()]
|
'args' => [
|
||||||
]],
|
'width' => ['type' => Type::int()],
|
||||||
|
'height' => ['type' => Type::int()],
|
||||||
|
],
|
||||||
|
],
|
||||||
'recentArticle' => ['type' => $article],
|
'recentArticle' => ['type' => $article],
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$reply = new ObjectType([
|
$reply = new ObjectType([
|
||||||
'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([
|
||||||
'name' => 'Article',
|
'name' => 'Article',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => ['type' => Type::string()],
|
'id' => ['type' => Type::string()],
|
||||||
'isPublished' => ['type' => Type::boolean()],
|
'isPublished' => ['type' => Type::boolean()],
|
||||||
'author' => ['type' => $author],
|
'author' => ['type' => $author],
|
||||||
'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 = '
|
||||||
query Test {
|
query Test {
|
||||||
article {
|
article {
|
||||||
author {
|
author {
|
||||||
@ -100,59 +106,70 @@ 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,
|
||||||
'author' => [
|
'author' => [
|
||||||
'id' => true,
|
'id' => true,
|
||||||
'name' => true,
|
'name' => true,
|
||||||
'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;
|
||||||
$actualDefaultSelection = null;
|
$actualDefaultSelection = null;
|
||||||
$actualDeepSelection = null;
|
$actualDeepSelection = null;
|
||||||
|
|
||||||
$blogQuery = new ObjectType([
|
$blogQuery = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'article' => [
|
'article' => [
|
||||||
'type' => $article,
|
'type' => $article,
|
||||||
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDefaultSelection, &$actualDeepSelection) {
|
'resolve' => function (
|
||||||
$hasCalled = true;
|
$value,
|
||||||
|
$args,
|
||||||
|
$context,
|
||||||
|
ResolveInfo $info
|
||||||
|
) use (
|
||||||
|
&$hasCalled,
|
||||||
|
&
|
||||||
|
$actualDefaultSelection,
|
||||||
|
&$actualDeepSelection
|
||||||
|
) {
|
||||||
|
$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]);
|
||||||
@ -167,50 +184,53 @@ class ResolveInfoTest extends TestCase
|
|||||||
public function testMergedFragmentsFieldSelection() : void
|
public function testMergedFragmentsFieldSelection() : void
|
||||||
{
|
{
|
||||||
$image = new ObjectType([
|
$image = new ObjectType([
|
||||||
'name' => 'Image',
|
'name' => 'Image',
|
||||||
'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' => [
|
||||||
'width' => ['type' => Type::int()],
|
'type' => $image,
|
||||||
'height' => ['type' => Type::int()]
|
'args' => [
|
||||||
]],
|
'width' => ['type' => Type::int()],
|
||||||
|
'height' => ['type' => Type::int()],
|
||||||
|
],
|
||||||
|
],
|
||||||
'recentArticle' => ['type' => $article],
|
'recentArticle' => ['type' => $article],
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$reply = new ObjectType([
|
$reply = new ObjectType([
|
||||||
'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([
|
||||||
'name' => 'Article',
|
'name' => 'Article',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'id' => ['type' => Type::string()],
|
'id' => ['type' => Type::string()],
|
||||||
'isPublished' => ['type' => Type::boolean()],
|
'isPublished' => ['type' => Type::boolean()],
|
||||||
'author' => ['type' => $author],
|
'author' => ['type' => $author],
|
||||||
'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 = '
|
||||||
@ -264,53 +284,63 @@ class ResolveInfoTest extends TestCase
|
|||||||
';
|
';
|
||||||
|
|
||||||
$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, //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
|
||||||
'author' => [
|
'author' => [
|
||||||
'id' => true,
|
'id' => true,
|
||||||
'name' => true,
|
'name' => true,
|
||||||
'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;
|
||||||
$actualDefaultSelection = null;
|
$actualDefaultSelection = null;
|
||||||
$actualDeepSelection = null;
|
$actualDeepSelection = null;
|
||||||
|
|
||||||
$blogQuery = new ObjectType([
|
$blogQuery = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'article' => [
|
'article' => [
|
||||||
'type' => $article,
|
'type' => $article,
|
||||||
'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDeepSelection) {
|
'resolve' => function (
|
||||||
$hasCalled = true;
|
$value,
|
||||||
|
$args,
|
||||||
|
$context,
|
||||||
|
ResolveInfo $info
|
||||||
|
) use (
|
||||||
|
&$hasCalled,
|
||||||
|
&
|
||||||
|
$actualDeepSelection
|
||||||
|
) {
|
||||||
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
@ -189,14 +186,13 @@ class ScalarSerializationTest extends TestCase
|
|||||||
{
|
{
|
||||||
$boolType = Type::boolean();
|
$boolType = Type::boolean();
|
||||||
|
|
||||||
$this->assertSame(true, $boolType->serialize('string'));
|
$this->assertTrue($boolType->serialize('string'));
|
||||||
$this->assertSame(false, $boolType->serialize(''));
|
$this->assertFalse($boolType->serialize(''));
|
||||||
$this->assertSame(true, $boolType->serialize('1'));
|
$this->assertTrue($boolType->serialize('1'));
|
||||||
$this->assertSame(true, $boolType->serialize(1));
|
$this->assertTrue($boolType->serialize(1));
|
||||||
$this->assertSame(false, $boolType->serialize(0));
|
$this->assertFalse($boolType->serialize(0));
|
||||||
$this->assertSame(true, $boolType->serialize(true));
|
$this->assertTrue($boolType->serialize(true));
|
||||||
$this->assertSame(false, $boolType->serialize(false));
|
$this->assertFalse($boolType->serialize(false));
|
||||||
|
|
||||||
// TODO: how should it behave on '0'?
|
// TODO: how should it behave on '0'?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
@ -28,20 +36,25 @@ class SchemaTest extends TestCase
|
|||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->interfaceType = new InterfaceType([
|
$this->interfaceType = new InterfaceType([
|
||||||
'name' => 'Interface',
|
'name' => 'Interface',
|
||||||
'fields' => ['fieldName' => ['type' => Type::string()]],
|
'fields' => ['fieldName' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$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' => [
|
||||||
return '';
|
'fieldName' => [
|
||||||
}]],
|
'type' => Type::string(),
|
||||||
|
'resolve' => function () {
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->directiveInputType = new InputObjectType([
|
$this->directiveInputType = new InputObjectType([
|
||||||
'name' => 'DirInput',
|
'name' => 'DirInput',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'field' => [
|
'field' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
@ -50,7 +63,7 @@ class SchemaTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->wrappedDirectiveInputType = new InputObjectType([
|
$this->wrappedDirectiveInputType = new InputObjectType([
|
||||||
'name' => 'WrappedDirInput',
|
'name' => 'WrappedDirInput',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'field' => [
|
'field' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
@ -59,10 +72,10 @@ class SchemaTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->directive = new Directive([
|
$this->directive = new Directive([
|
||||||
'name' => 'dir',
|
'name' => 'dir',
|
||||||
'locations' => ['OBJECT'],
|
'locations' => ['OBJECT'],
|
||||||
'args' => [
|
'args' => [
|
||||||
'arg' => [
|
'arg' => [
|
||||||
'type' => $this->directiveInputType,
|
'type' => $this->directiveInputType,
|
||||||
],
|
],
|
||||||
'argList' => [
|
'argList' => [
|
||||||
@ -72,11 +85,11 @@ class SchemaTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->schema = new Schema([
|
$this->schema = new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'getObject' => [
|
'getObject' => [
|
||||||
'type' => $this->interfaceType,
|
'type' => $this->interfaceType,
|
||||||
'resolve' => function () {
|
'resolve' => function () {
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
21
tests/Type/TestClasses/MyCustomType.php
Normal file
21
tests/Type/TestClasses/MyCustomType.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
24
tests/Type/TestClasses/OtherCustom.php
Normal file
24
tests/Type/TestClasses/OtherCustom.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
@ -62,36 +47,41 @@ class TypeLoaderTest extends TestCase
|
|||||||
$this->calls = [];
|
$this->calls = [];
|
||||||
|
|
||||||
$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'),
|
||||||
@ -101,53 +91,56 @@ 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([
|
||||||
'name' => 'PostStoryMutationInput',
|
'name' => 'PostStoryMutationInput',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,11 +148,12 @@ class TypeLoaderTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->expectNotToPerformAssertions();
|
$this->expectNotToPerformAssertions();
|
||||||
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 () {
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,20 +163,20 @@ class TypeLoaderTest extends TestCase
|
|||||||
$this->expectExceptionMessage('Schema type loader must be callable if provided but got: []');
|
$this->expectExceptionMessage('Schema type loader must be callable if provided but got: []');
|
||||||
|
|
||||||
new Schema([
|
new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => ['a' => Type::string()]
|
'fields' => ['a' => Type::string()],
|
||||||
]),
|
]),
|
||||||
'typeLoader' => []
|
'typeLoader' => [],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWorksWithoutTypeLoader() : void
|
public function testWorksWithoutTypeLoader() : void
|
||||||
{
|
{
|
||||||
$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 = [
|
||||||
@ -203,12 +197,12 @@ class TypeLoaderTest extends TestCase
|
|||||||
$this->assertSame($this->postStoryMutationInput, $schema->getType('PostStoryMutationInput'));
|
$this->assertSame($this->postStoryMutationInput, $schema->getType('PostStoryMutationInput'));
|
||||||
|
|
||||||
$expectedTypeMap = [
|
$expectedTypeMap = [
|
||||||
'Query' => $this->query,
|
'Query' => $this->query,
|
||||||
'Mutation' => $this->mutation,
|
'Mutation' => $this->mutation,
|
||||||
'Node' => $this->node,
|
'Node' => $this->node,
|
||||||
'String' => Type::string(),
|
'String' => Type::string(),
|
||||||
'Content' => $this->content,
|
'Content' => $this->content,
|
||||||
'BlogStory' => $this->blogStory,
|
'BlogStory' => $this->blogStory,
|
||||||
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -218,9 +212,9 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testWorksWithTypeLoader() : void
|
public function testWorksWithTypeLoader() : void
|
||||||
{
|
{
|
||||||
$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);
|
||||||
|
|
||||||
@ -244,8 +238,8 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testOnlyCallsLoaderOnce() : void
|
public function testOnlyCallsLoaderOnce() : void
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->query,
|
'query' => $this->query,
|
||||||
'typeLoader' => $this->typeLoader
|
'typeLoader' => $this->typeLoader,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema->getType('Node');
|
$schema->getType('Node');
|
||||||
@ -258,8 +252,9 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testFailsOnNonExistentType() : void
|
public function testFailsOnNonExistentType() : void
|
||||||
{
|
{
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $this->query,
|
'query' => $this->query,
|
||||||
'typeLoader' => function() {}
|
'typeLoader' => function () {
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
@ -271,10 +266,10 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testFailsOnNonType() : void
|
public function testFailsOnNonType() : void
|
||||||
{
|
{
|
||||||
$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);
|
||||||
@ -286,10 +281,10 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testFailsOnInvalidLoad() : void
|
public function testFailsOnInvalidLoad() : void
|
||||||
{
|
{
|
||||||
$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);
|
||||||
@ -301,10 +296,10 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testPassesThroughAnExceptionInLoader() : void
|
public function testPassesThroughAnExceptionInLoader() : void
|
||||||
{
|
{
|
||||||
$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);
|
||||||
@ -316,14 +311,14 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testReturnsIdenticalResults() : void
|
public function testReturnsIdenticalResults() : void
|
||||||
{
|
{
|
||||||
$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());
|
||||||
@ -335,9 +330,9 @@ class TypeLoaderTest extends TestCase
|
|||||||
public function testSkipsLoaderForInternalTypes() : void
|
public function testSkipsLoaderForInternalTypes() : void
|
||||||
{
|
{
|
||||||
$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
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -9,7 +12,6 @@ use PHPUnit\Framework\TestCase;
|
|||||||
class AssertValidNameTest extends TestCase
|
class AssertValidNameTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: assertValidName()
|
// Describe: assertValidName()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('throws for use of leading double underscores')
|
* @see it('throws for use of leading double underscores')
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Language\AST\BooleanValueNode;
|
use GraphQL\Language\AST\BooleanValueNode;
|
||||||
@ -16,10 +19,12 @@ use GraphQL\Type\Definition\InputObjectType;
|
|||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Utils\AST;
|
use GraphQL\Utils\AST;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
class AstFromValueTest extends TestCase
|
class AstFromValueTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: astFromValue
|
/** @var stdClass */
|
||||||
|
private $complexValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('converts boolean values to ASTs')
|
* @see it('converts boolean values to ASTs')
|
||||||
@ -31,8 +36,14 @@ class AstFromValueTest extends TestCase
|
|||||||
$this->assertEquals(new NullValueNode([]), AST::astFromValue(null, Type::boolean()));
|
$this->assertEquals(new NullValueNode([]), AST::astFromValue(null, Type::boolean()));
|
||||||
$this->assertEquals(new BooleanValueNode(['value' => false]), AST::astFromValue(0, Type::boolean()));
|
$this->assertEquals(new BooleanValueNode(['value' => false]), AST::astFromValue(0, Type::boolean()));
|
||||||
$this->assertEquals(new BooleanValueNode(['value' => true]), AST::astFromValue(1, Type::boolean()));
|
$this->assertEquals(new BooleanValueNode(['value' => true]), AST::astFromValue(1, Type::boolean()));
|
||||||
$this->assertEquals(new BooleanValueNode(['value' => false]), AST::astFromValue(0, Type::nonNull(Type::boolean())));
|
$this->assertEquals(
|
||||||
$this->assertEquals(null, AST::astFromValue(null, Type::nonNull(Type::boolean()))); // Note: null means that AST cannot
|
new BooleanValueNode(['value' => false]),
|
||||||
|
AST::astFromValue(0, Type::nonNull(Type::boolean()))
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
null,
|
||||||
|
AST::astFromValue(null, Type::nonNull(Type::boolean()))
|
||||||
|
); // Note: null means that AST cannot
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +70,10 @@ class AstFromValueTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->expectException(\Throwable::class);
|
$this->expectException(\Throwable::class);
|
||||||
$this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 1.0E+40');
|
$this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 1.0E+40');
|
||||||
AST::astFromValue(1e40, Type::int()); // Note: js version will produce 1e+40, both values are valid GraphQL floats
|
AST::astFromValue(
|
||||||
|
1e40,
|
||||||
|
Type::int()
|
||||||
|
); // Note: js version will produce 1e+40, both values are valid GraphQL floats
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,7 +126,7 @@ class AstFromValueTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDoesNotConvertsNonNullValuestoNullValue() : void
|
public function testDoesNotConvertsNonNullValuestoNullValue() : void
|
||||||
{
|
{
|
||||||
$this->assertSame(null, AST::astFromValue(null, Type::nonNull(Type::boolean())));
|
$this->assertNull(AST::astFromValue(null, Type::nonNull(Type::boolean())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,13 +135,41 @@ class AstFromValueTest extends TestCase
|
|||||||
public function testConvertsStringValuesToEnumASTsIfPossible() : void
|
public function testConvertsStringValuesToEnumASTsIfPossible() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals(new EnumValueNode(['value' => 'HELLO']), AST::astFromValue('HELLO', $this->myEnum()));
|
$this->assertEquals(new EnumValueNode(['value' => 'HELLO']), AST::astFromValue('HELLO', $this->myEnum()));
|
||||||
$this->assertEquals(new EnumValueNode(['value' => 'COMPLEX']), AST::astFromValue($this->complexValue(), $this->myEnum()));
|
$this->assertEquals(
|
||||||
|
new EnumValueNode(['value' => 'COMPLEX']),
|
||||||
|
AST::astFromValue($this->complexValue(), $this->myEnum())
|
||||||
|
);
|
||||||
|
|
||||||
// Note: case sensitive
|
// Note: case sensitive
|
||||||
$this->assertEquals(null, AST::astFromValue('hello', $this->myEnum()));
|
$this->assertNull(AST::astFromValue('hello', $this->myEnum()));
|
||||||
|
|
||||||
// Note: Not a valid enum value
|
// Note: Not a valid enum value
|
||||||
$this->assertEquals(null, AST::astFromValue('VALUE', $this->myEnum()));
|
$this->assertNull(AST::astFromValue('VALUE', $this->myEnum()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return EnumType
|
||||||
|
*/
|
||||||
|
private function myEnum()
|
||||||
|
{
|
||||||
|
return new EnumType([
|
||||||
|
'name' => 'MyEnum',
|
||||||
|
'values' => [
|
||||||
|
'HELLO' => [],
|
||||||
|
'GOODBYE' => [],
|
||||||
|
'COMPLEX' => ['value' => $this->complexValue()],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function complexValue()
|
||||||
|
{
|
||||||
|
if (! $this->complexValue) {
|
||||||
|
$this->complexValue = new \stdClass();
|
||||||
|
$this->complexValue->someArbitrary = 'complexValue';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->complexValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,8 +180,8 @@ class AstFromValueTest extends TestCase
|
|||||||
$value1 = new ListValueNode([
|
$value1 = new ListValueNode([
|
||||||
'values' => [
|
'values' => [
|
||||||
new StringValueNode(['value' => 'FOO']),
|
new StringValueNode(['value' => 'FOO']),
|
||||||
new StringValueNode(['value' => 'BAR'])
|
new StringValueNode(['value' => 'BAR']),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals($value1, AST::astFromValue(['FOO', 'BAR'], Type::listOf(Type::string())));
|
$this->assertEquals($value1, AST::astFromValue(['FOO', 'BAR'], Type::listOf(Type::string())));
|
||||||
|
|
||||||
@ -147,7 +189,7 @@ class AstFromValueTest extends TestCase
|
|||||||
'values' => [
|
'values' => [
|
||||||
new EnumValueNode(['value' => 'HELLO']),
|
new EnumValueNode(['value' => 'HELLO']),
|
||||||
new EnumValueNode(['value' => 'GOODBYE']),
|
new EnumValueNode(['value' => 'GOODBYE']),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals($value2, AST::astFromValue(['HELLO', 'GOODBYE'], Type::listOf($this->myEnum())));
|
$this->assertEquals($value2, AST::astFromValue(['HELLO', 'GOODBYE'], Type::listOf($this->myEnum())));
|
||||||
}
|
}
|
||||||
@ -157,7 +199,10 @@ class AstFromValueTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testConvertsListSingletons() : void
|
public function testConvertsListSingletons() : void
|
||||||
{
|
{
|
||||||
$this->assertEquals(new StringValueNode(['value' => 'FOO']), AST::astFromValue('FOO', Type::listOf(Type::string())));
|
$this->assertEquals(
|
||||||
|
new StringValueNode(['value' => 'FOO']),
|
||||||
|
AST::astFromValue('FOO', Type::listOf(Type::string()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,18 +211,18 @@ class AstFromValueTest extends TestCase
|
|||||||
public function testConvertsInputObjects() : void
|
public function testConvertsInputObjects() : void
|
||||||
{
|
{
|
||||||
$inputObj = new InputObjectType([
|
$inputObj = new InputObjectType([
|
||||||
'name' => 'MyInputObj',
|
'name' => 'MyInputObj',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'foo' => Type::float(),
|
'foo' => Type::float(),
|
||||||
'bar' => $this->myEnum()
|
'bar' => $this->myEnum(),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$expected = new ObjectValueNode([
|
$expected = new ObjectValueNode([
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->objectField('foo', new IntValueNode(['value' => '3'])),
|
$this->objectField('foo', new IntValueNode(['value' => '3'])),
|
||||||
$this->objectField('bar', new EnumValueNode(['value' => 'HELLO']))
|
$this->objectField('bar', new EnumValueNode(['value' => 'HELLO'])),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data = ['foo' => 3, 'bar' => 'HELLO'];
|
$data = ['foo' => 3, 'bar' => 'HELLO'];
|
||||||
@ -185,62 +230,38 @@ class AstFromValueTest extends TestCase
|
|||||||
$this->assertEquals($expected, AST::astFromValue((object) $data, $inputObj));
|
$this->assertEquals($expected, AST::astFromValue((object) $data, $inputObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @return ObjectFieldNode
|
||||||
|
*/
|
||||||
|
private function objectField(string $name, $value)
|
||||||
|
{
|
||||||
|
return new ObjectFieldNode([
|
||||||
|
'name' => new NameNode(['value' => $name]),
|
||||||
|
'value' => $value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('converts input objects with explicit nulls')
|
* @see it('converts input objects with explicit nulls')
|
||||||
*/
|
*/
|
||||||
public function testConvertsInputObjectsWithExplicitNulls() : void
|
public function testConvertsInputObjectsWithExplicitNulls() : void
|
||||||
{
|
{
|
||||||
$inputObj = new InputObjectType([
|
$inputObj = new InputObjectType([
|
||||||
'name' => 'MyInputObj',
|
'name' => 'MyInputObj',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'foo' => Type::float(),
|
'foo' => Type::float(),
|
||||||
'bar' => $this->myEnum()
|
'bar' => $this->myEnum(),
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(new ObjectValueNode([
|
$this->assertEquals(
|
||||||
|
new ObjectValueNode([
|
||||||
'fields' => [
|
'fields' => [
|
||||||
$this->objectField('foo', new NullValueNode([]))
|
$this->objectField('foo', new NullValueNode([])),
|
||||||
]
|
],
|
||||||
]), AST::astFromValue(['foo' => null], $inputObj));
|
]),
|
||||||
}
|
AST::astFromValue(['foo' => null], $inputObj)
|
||||||
|
);
|
||||||
private $complexValue;
|
|
||||||
|
|
||||||
private function complexValue()
|
|
||||||
{
|
|
||||||
if (!$this->complexValue) {
|
|
||||||
$this->complexValue = new \stdClass();
|
|
||||||
$this->complexValue->someArbitrary = 'complexValue';
|
|
||||||
}
|
|
||||||
return $this->complexValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return EnumType
|
|
||||||
*/
|
|
||||||
private function myEnum()
|
|
||||||
{
|
|
||||||
return new EnumType([
|
|
||||||
'name' => 'MyEnum',
|
|
||||||
'values' => [
|
|
||||||
'HELLO' => [],
|
|
||||||
'GOODBYE' => [],
|
|
||||||
'COMPLEX' => ['value' => $this->complexValue()]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
* @param $value
|
|
||||||
* @return ObjectFieldNode
|
|
||||||
*/
|
|
||||||
private function objectField($name, $value)
|
|
||||||
{
|
|
||||||
return new ObjectFieldNode([
|
|
||||||
'name' => new NameNode(['value' => $name]),
|
|
||||||
'value' => $value
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
@ -8,14 +11,6 @@ use PHPUnit\Framework\TestCase;
|
|||||||
class AstFromValueUntypedTest extends TestCase
|
class AstFromValueUntypedTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: valueFromASTUntyped
|
// Describe: valueFromASTUntyped
|
||||||
|
|
||||||
private function assertTestCase($valueText, $expected, array $variables = null) {
|
|
||||||
$this->assertEquals(
|
|
||||||
$expected,
|
|
||||||
AST::valueFromASTUntyped(Parser::parseValue($valueText), $variables)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('parses simple values')
|
* @see it('parses simple values')
|
||||||
*/
|
*/
|
||||||
@ -29,6 +24,17 @@ class AstFromValueUntypedTest extends TestCase
|
|||||||
$this->assertTestCase('abc123', 'abc123');
|
$this->assertTestCase('abc123', 'abc123');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[]|null $variables
|
||||||
|
*/
|
||||||
|
private function assertTestCase($valueText, $expected, ?array $variables = null) : void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$expected,
|
||||||
|
AST::valueFromASTUntyped(Parser::parseValue($valueText), $variables)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('parses lists of values')
|
* @see it('parses lists of values')
|
||||||
*/
|
*/
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
@ -8,24 +11,19 @@ use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
|
|||||||
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\Printer;
|
use GraphQL\Language\Printer;
|
||||||
|
use GraphQL\Type\Definition\Directive;
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Utils\BuildSchema;
|
use GraphQL\Utils\BuildSchema;
|
||||||
use GraphQL\Utils\SchemaPrinter;
|
use GraphQL\Utils\SchemaPrinter;
|
||||||
use GraphQL\Type\Definition\Directive;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use function array_keys;
|
||||||
|
use function count;
|
||||||
|
|
||||||
class BuildSchemaTest extends TestCase
|
class BuildSchemaTest extends TestCase
|
||||||
{
|
{
|
||||||
// Describe: Schema Builder
|
// Describe: Schema Builder
|
||||||
|
|
||||||
private function cycleOutput($body, $options = [])
|
|
||||||
{
|
|
||||||
$ast = Parser::parse($body);
|
|
||||||
$schema = BuildSchema::buildAST($ast, null, $options);
|
|
||||||
return "\n" . SchemaPrinter::doPrint($schema, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('can use built schema for limited execution')
|
* @see it('can use built schema for limited execution')
|
||||||
*/
|
*/
|
||||||
@ -46,16 +44,16 @@ class BuildSchemaTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testBuildSchemaDirectlyFromSource() : void
|
public function testBuildSchemaDirectlyFromSource() : void
|
||||||
{
|
{
|
||||||
$schema = BuildSchema::build("
|
$schema = BuildSchema::build('
|
||||||
type Query {
|
type Query {
|
||||||
add(x: Int, y: Int): Int
|
add(x: Int, y: Int): Int
|
||||||
}
|
}
|
||||||
");
|
');
|
||||||
|
|
||||||
$root = [
|
$root = [
|
||||||
'add' => function ($root, $args) {
|
'add' => function ($root, $args) {
|
||||||
return $args['x'] + $args['y'];
|
return $args['x'] + $args['y'];
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = GraphQL::executeQuery(
|
$result = GraphQL::executeQuery(
|
||||||
@ -71,7 +69,7 @@ class BuildSchemaTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testSimpleType() : void
|
public function testSimpleType() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type HelloScalars {
|
type HelloScalars {
|
||||||
str: String
|
str: String
|
||||||
int: Int
|
int: Int
|
||||||
@ -84,12 +82,20 @@ type HelloScalars {
|
|||||||
$this->assertEquals($output, $body);
|
$this->assertEquals($output, $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function cycleOutput($body, $options = [])
|
||||||
|
{
|
||||||
|
$ast = Parser::parse($body);
|
||||||
|
$schema = BuildSchema::buildAST($ast, null, $options);
|
||||||
|
|
||||||
|
return "\n" . SchemaPrinter::doPrint($schema, $options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('With directives')
|
* @see it('With directives')
|
||||||
*/
|
*/
|
||||||
public function testWithDirectives() : void
|
public function testWithDirectives() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
directive @foo(arg: Int) on FIELD
|
directive @foo(arg: Int) on FIELD
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -105,7 +111,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSupportsDescriptions() : void
|
public function testSupportsDescriptions() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
"""This is a directive"""
|
"""This is a directive"""
|
||||||
directive @foo(
|
directive @foo(
|
||||||
"""It has an argument"""
|
"""It has an argument"""
|
||||||
@ -137,7 +143,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSupportsOptionForCommentDescriptions() : void
|
public function testSupportsOptionForCommentDescriptions() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
# This is a directive
|
# This is a directive
|
||||||
directive @foo(
|
directive @foo(
|
||||||
# It has an argument
|
# It has an argument
|
||||||
@ -159,7 +165,7 @@ type Query {
|
|||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$output = $this->cycleOutput($body, [ 'commentDescriptions' => true ]);
|
$output = $this->cycleOutput($body, ['commentDescriptions' => true]);
|
||||||
$this->assertEquals($body, $output);
|
$this->assertEquals($body, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +174,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testMaintainsSkipAndInclude() : void
|
public function testMaintainsSkipAndInclude() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query {
|
type Query {
|
||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
@ -185,7 +191,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testOverridingDirectivesExcludesSpecified() : void
|
public function testOverridingDirectivesExcludesSpecified() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
directive @skip on FIELD
|
directive @skip on FIELD
|
||||||
directive @include on FIELD
|
directive @include on FIELD
|
||||||
directive @deprecated on FIELD_DEFINITION
|
directive @deprecated on FIELD_DEFINITION
|
||||||
@ -206,7 +212,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testAddingDirectivesMaintainsSkipAndInclude() : void
|
public function testAddingDirectivesMaintainsSkipAndInclude() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
directive @foo(arg: Int) on FIELD
|
directive @foo(arg: Int) on FIELD
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -225,7 +231,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testTypeModifiers() : void
|
public function testTypeModifiers() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type HelloScalars {
|
type HelloScalars {
|
||||||
nonNullStr: String!
|
nonNullStr: String!
|
||||||
listOfStrs: [String]
|
listOfStrs: [String]
|
||||||
@ -243,7 +249,7 @@ type HelloScalars {
|
|||||||
*/
|
*/
|
||||||
public function testRecursiveType() : void
|
public function testRecursiveType() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query {
|
type Query {
|
||||||
str: String
|
str: String
|
||||||
recurse: Query
|
recurse: Query
|
||||||
@ -258,7 +264,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testTwoTypesCircular() : void
|
public function testTwoTypesCircular() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
schema {
|
schema {
|
||||||
query: TypeOne
|
query: TypeOne
|
||||||
}
|
}
|
||||||
@ -282,7 +288,7 @@ type TypeTwo {
|
|||||||
*/
|
*/
|
||||||
public function testSingleArgumentField() : void
|
public function testSingleArgumentField() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query {
|
type Query {
|
||||||
str(int: Int): String
|
str(int: Int): String
|
||||||
floatToStr(float: Float): String
|
floatToStr(float: Float): String
|
||||||
@ -300,7 +306,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleTypeWithMultipleArguments() : void
|
public function testSimpleTypeWithMultipleArguments() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query {
|
type Query {
|
||||||
str(int: Int, bool: Boolean): String
|
str(int: Int, bool: Boolean): String
|
||||||
}
|
}
|
||||||
@ -314,7 +320,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleTypeWithInterface() : void
|
public function testSimpleTypeWithInterface() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query implements WorldInterface {
|
type Query implements WorldInterface {
|
||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
@ -332,7 +338,7 @@ interface WorldInterface {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleOutputEnum() : void
|
public function testSimpleOutputEnum() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
enum Hello {
|
enum Hello {
|
||||||
WORLD
|
WORLD
|
||||||
}
|
}
|
||||||
@ -350,7 +356,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleInputEnum() : void
|
public function testSimpleInputEnum() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
enum Hello {
|
enum Hello {
|
||||||
WORLD
|
WORLD
|
||||||
}
|
}
|
||||||
@ -368,7 +374,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testMultipleValueEnum() : void
|
public function testMultipleValueEnum() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
enum Hello {
|
enum Hello {
|
||||||
WO
|
WO
|
||||||
RLD
|
RLD
|
||||||
@ -387,7 +393,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleUnion() : void
|
public function testSimpleUnion() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
union Hello = World
|
union Hello = World
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -407,7 +413,7 @@ type World {
|
|||||||
*/
|
*/
|
||||||
public function testMultipleUnion() : void
|
public function testMultipleUnion() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
union Hello = WorldOne | WorldTwo
|
union Hello = WorldOne | WorldTwo
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -431,7 +437,7 @@ type WorldTwo {
|
|||||||
*/
|
*/
|
||||||
public function testSpecifyingUnionTypeUsingTypename() : void
|
public function testSpecifyingUnionTypeUsingTypename() : void
|
||||||
{
|
{
|
||||||
$schema = BuildSchema::buildAST(Parser::parse('
|
$schema = BuildSchema::buildAST(Parser::parse('
|
||||||
type Query {
|
type Query {
|
||||||
fruits: [Fruit]
|
fruits: [Fruit]
|
||||||
}
|
}
|
||||||
@ -446,7 +452,7 @@ type WorldTwo {
|
|||||||
length: Int
|
length: Int
|
||||||
}
|
}
|
||||||
'));
|
'));
|
||||||
$query = '
|
$query = '
|
||||||
{
|
{
|
||||||
fruits {
|
fruits {
|
||||||
... on Apple {
|
... on Apple {
|
||||||
@ -458,25 +464,25 @@ type WorldTwo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$root = [
|
$root = [
|
||||||
'fruits' => [
|
'fruits' => [
|
||||||
[
|
[
|
||||||
'color' => 'green',
|
'color' => 'green',
|
||||||
'__typename' => 'Apple',
|
'__typename' => 'Apple',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'length' => 5,
|
'length' => 5,
|
||||||
'__typename' => 'Banana',
|
'__typename' => 'Banana',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'fruits' => [
|
'fruits' => [
|
||||||
['color' => 'green'],
|
['color' => 'green'],
|
||||||
['length' => 5],
|
['length' => 5],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = GraphQL::executeQuery($schema, $query, $root);
|
$result = GraphQL::executeQuery($schema, $query, $root);
|
||||||
@ -488,7 +494,7 @@ type WorldTwo {
|
|||||||
*/
|
*/
|
||||||
public function testSpecifyingInterfaceUsingTypename() : void
|
public function testSpecifyingInterfaceUsingTypename() : void
|
||||||
{
|
{
|
||||||
$schema = BuildSchema::buildAST(Parser::parse('
|
$schema = BuildSchema::buildAST(Parser::parse('
|
||||||
type Query {
|
type Query {
|
||||||
characters: [Character]
|
characters: [Character]
|
||||||
}
|
}
|
||||||
@ -507,7 +513,7 @@ type WorldTwo {
|
|||||||
primaryFunction: String
|
primaryFunction: String
|
||||||
}
|
}
|
||||||
'));
|
'));
|
||||||
$query = '
|
$query = '
|
||||||
{
|
{
|
||||||
characters {
|
characters {
|
||||||
name
|
name
|
||||||
@ -520,27 +526,27 @@ type WorldTwo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$root = [
|
$root = [
|
||||||
'characters' => [
|
'characters' => [
|
||||||
[
|
[
|
||||||
'name' => 'Han Solo',
|
'name' => 'Han Solo',
|
||||||
'totalCredits' => 10,
|
'totalCredits' => 10,
|
||||||
'__typename' => 'Human',
|
'__typename' => 'Human',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'R2-D2',
|
'name' => 'R2-D2',
|
||||||
'primaryFunction' => 'Astromech',
|
'primaryFunction' => 'Astromech',
|
||||||
'__typename' => 'Droid',
|
'__typename' => 'Droid',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'characters' => [
|
'characters' => [
|
||||||
['name' => 'Han Solo', 'totalCredits' => 10],
|
['name' => 'Han Solo', 'totalCredits' => 10],
|
||||||
['name' => 'R2-D2', 'primaryFunction' => 'Astromech'],
|
['name' => 'R2-D2', 'primaryFunction' => 'Astromech'],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = GraphQL::executeQuery($schema, $query, $root);
|
$result = GraphQL::executeQuery($schema, $query, $root);
|
||||||
@ -552,7 +558,7 @@ type WorldTwo {
|
|||||||
*/
|
*/
|
||||||
public function testCustomScalar() : void
|
public function testCustomScalar() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
scalar CustomScalar
|
scalar CustomScalar
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -568,7 +574,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testInputObject() : void
|
public function testInputObject() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
input Input {
|
input Input {
|
||||||
int: Int
|
int: Int
|
||||||
}
|
}
|
||||||
@ -586,7 +592,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleArgumentFieldWithDefault() : void
|
public function testSimpleArgumentFieldWithDefault() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Query {
|
type Query {
|
||||||
str(int: Int = 2): String
|
str(int: Int = 2): String
|
||||||
}
|
}
|
||||||
@ -600,7 +606,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testCustomScalarArgumentFieldWithDefault() : void
|
public function testCustomScalarArgumentFieldWithDefault() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
scalar CustomScalar
|
scalar CustomScalar
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
@ -616,7 +622,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleTypeWithMutation() : void
|
public function testSimpleTypeWithMutation() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
schema {
|
schema {
|
||||||
query: HelloScalars
|
query: HelloScalars
|
||||||
mutation: Mutation
|
mutation: Mutation
|
||||||
@ -641,7 +647,7 @@ type Mutation {
|
|||||||
*/
|
*/
|
||||||
public function testSimpleTypeWithSubscription() : void
|
public function testSimpleTypeWithSubscription() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
schema {
|
schema {
|
||||||
query: HelloScalars
|
query: HelloScalars
|
||||||
subscription: Subscription
|
subscription: Subscription
|
||||||
@ -666,7 +672,7 @@ type Subscription {
|
|||||||
*/
|
*/
|
||||||
public function testUnreferencedTypeImplementingReferencedInterface() : void
|
public function testUnreferencedTypeImplementingReferencedInterface() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Concrete implements Iface {
|
type Concrete implements Iface {
|
||||||
key: String
|
key: String
|
||||||
}
|
}
|
||||||
@ -688,7 +694,7 @@ type Query {
|
|||||||
*/
|
*/
|
||||||
public function testUnreferencedTypeImplementingReferencedUnion() : void
|
public function testUnreferencedTypeImplementingReferencedUnion() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
type Concrete {
|
type Concrete {
|
||||||
key: String
|
key: String
|
||||||
}
|
}
|
||||||
@ -708,7 +714,7 @@ union Union = Concrete
|
|||||||
*/
|
*/
|
||||||
public function testSupportsDeprecated() : void
|
public function testSupportsDeprecated() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
enum MyEnum {
|
enum MyEnum {
|
||||||
VALUE
|
VALUE
|
||||||
OLD_VALUE @deprecated
|
OLD_VALUE @deprecated
|
||||||
@ -724,7 +730,7 @@ type Query {
|
|||||||
$output = $this->cycleOutput($body);
|
$output = $this->cycleOutput($body);
|
||||||
$this->assertEquals($output, $body);
|
$this->assertEquals($output, $body);
|
||||||
|
|
||||||
$ast = Parser::parse($body);
|
$ast = Parser::parse($body);
|
||||||
$schema = BuildSchema::buildAST($ast);
|
$schema = BuildSchema::buildAST($ast);
|
||||||
|
|
||||||
/** @var EnumType $myEnum */
|
/** @var EnumType $myEnum */
|
||||||
@ -785,15 +791,15 @@ type Query {
|
|||||||
|
|
||||||
directive @test(arg: TestScalar) on FIELD
|
directive @test(arg: TestScalar) on FIELD
|
||||||
');
|
');
|
||||||
$schema = BuildSchema::buildAST($schemaAST);
|
$schema = BuildSchema::buildAST($schemaAST);
|
||||||
/** @var ObjectType $query */
|
/** @var ObjectType $query */
|
||||||
$query = $schema->getType('Query');
|
$query = $schema->getType('Query');
|
||||||
$testInput = $schema->getType('TestInput');
|
$testInput = $schema->getType('TestInput');
|
||||||
$testEnum = $schema->getType('TestEnum');
|
$testEnum = $schema->getType('TestEnum');
|
||||||
$testUnion = $schema->getType('TestUnion');
|
$testUnion = $schema->getType('TestUnion');
|
||||||
$testInterface = $schema->getType('TestInterface');
|
$testInterface = $schema->getType('TestInterface');
|
||||||
$testType = $schema->getType('TestType');
|
$testType = $schema->getType('TestType');
|
||||||
$testScalar = $schema->getType('TestScalar');
|
$testScalar = $schema->getType('TestScalar');
|
||||||
$testDirective = $schema->getDirective('test');
|
$testDirective = $schema->getDirective('test');
|
||||||
|
|
||||||
$restoredIDL = SchemaPrinter::doPrint(BuildSchema::build(
|
$restoredIDL = SchemaPrinter::doPrint(BuildSchema::build(
|
||||||
@ -813,9 +819,15 @@ type Query {
|
|||||||
$testField = $query->getField('testField');
|
$testField = $query->getField('testField');
|
||||||
$this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
|
$this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode));
|
||||||
$this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
|
$this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode));
|
||||||
$this->assertEquals('testInputField: TestEnum', Printer::doPrint($testInput->getField('testInputField')->astNode));
|
$this->assertEquals(
|
||||||
|
'testInputField: TestEnum',
|
||||||
|
Printer::doPrint($testInput->getField('testInputField')->astNode)
|
||||||
|
);
|
||||||
$this->assertEquals('TEST_VALUE', Printer::doPrint($testEnum->getValue('TEST_VALUE')->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($testInterface->getField('interfaceField')->astNode)
|
||||||
|
);
|
||||||
$this->assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode));
|
$this->assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode));
|
||||||
$this->assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode));
|
$this->assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode));
|
||||||
}
|
}
|
||||||
@ -893,7 +905,7 @@ type Hello {
|
|||||||
bar: Bar
|
bar: Bar
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,7 +930,7 @@ type Yellow {
|
|||||||
isColor: Boolean
|
isColor: Boolean
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,7 +956,7 @@ type Yellow {
|
|||||||
isColor: Boolean
|
isColor: Boolean
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,7 +982,7 @@ type Yellow {
|
|||||||
isColor: Boolean
|
isColor: Boolean
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,7 +993,7 @@ type Yellow {
|
|||||||
{
|
{
|
||||||
$this->expectException(Error::class);
|
$this->expectException(Error::class);
|
||||||
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
||||||
$body = '
|
$body = '
|
||||||
schema {
|
schema {
|
||||||
query: Hello
|
query: Hello
|
||||||
}
|
}
|
||||||
@ -990,7 +1002,7 @@ type Hello {
|
|||||||
bar: Bar
|
bar: Bar
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$schema = BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
$schema->getTypeMap();
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
@ -1002,12 +1014,12 @@ type Hello {
|
|||||||
{
|
{
|
||||||
$this->expectException(Error::class);
|
$this->expectException(Error::class);
|
||||||
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
||||||
$body = '
|
$body = '
|
||||||
type Query implements Bar {
|
type Query implements Bar {
|
||||||
field: String
|
field: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$schema = BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
$schema->getTypeMap();
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
@ -1019,11 +1031,11 @@ type Query implements Bar {
|
|||||||
{
|
{
|
||||||
$this->expectException(Error::class);
|
$this->expectException(Error::class);
|
||||||
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
$this->expectExceptionMessage('Type "Bar" not found in document.');
|
||||||
$body = '
|
$body = '
|
||||||
union TestUnion = Bar
|
union TestUnion = Bar
|
||||||
type Query { testUnion: TestUnion }
|
type Query { testUnion: TestUnion }
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$schema = BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
$schema->getTypeMap();
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
@ -1044,7 +1056,7 @@ type Hello {
|
|||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1065,7 +1077,7 @@ type Hello {
|
|||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,7 +1103,7 @@ type Wat {
|
|||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,7 +1121,7 @@ schema {
|
|||||||
|
|
||||||
query Foo { field }
|
query Foo { field }
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,7 +1139,7 @@ schema {
|
|||||||
|
|
||||||
fragment Foo on Type { field }
|
fragment Foo on Type { field }
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1149,7 +1161,7 @@ type Repeated {
|
|||||||
id: String
|
id: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$this->expectException(Error::class);
|
$this->expectException(Error::class);
|
||||||
$this->expectExceptionMessage('Type "Repeated" was defined more than once.');
|
$this->expectExceptionMessage('Type "Repeated" was defined more than once.');
|
||||||
@ -1179,14 +1191,15 @@ interface Hello {
|
|||||||
world: String
|
world: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
$decorated = [];
|
$decorated = [];
|
||||||
$calls = [];
|
$calls = [];
|
||||||
|
|
||||||
$typeConfigDecorator = function($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) {
|
$typeConfigDecorator = function ($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) {
|
||||||
$decorated[] = $defaultConfig['name'];
|
$decorated[] = $defaultConfig['name'];
|
||||||
$calls[] = [$defaultConfig, $node, $allNodesMap];
|
$calls[] = [$defaultConfig, $node, $allNodesMap];
|
||||||
|
|
||||||
return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
|
return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1204,19 +1217,21 @@ interface Hello {
|
|||||||
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
||||||
$this->assertEquals('My description of Query', $schema->getType('Query')->description);
|
$this->assertEquals('My description of Query', $schema->getType('Query')->description);
|
||||||
|
|
||||||
|
|
||||||
list($defaultConfig, $node, $allNodesMap) = $calls[1];
|
list($defaultConfig, $node, $allNodesMap) = $calls[1];
|
||||||
$this->assertInstanceOf(EnumTypeDefinitionNode::class, $node);
|
$this->assertInstanceOf(EnumTypeDefinitionNode::class, $node);
|
||||||
$this->assertEquals('Color', $defaultConfig['name']);
|
$this->assertEquals('Color', $defaultConfig['name']);
|
||||||
$enumValue = [
|
$enumValue = [
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'deprecationReason' => ''
|
'deprecationReason' => '',
|
||||||
];
|
];
|
||||||
$this->assertArraySubset([
|
$this->assertArraySubset(
|
||||||
'RED' => $enumValue,
|
[
|
||||||
'GREEN' => $enumValue,
|
'RED' => $enumValue,
|
||||||
'BLUE' => $enumValue,
|
'GREEN' => $enumValue,
|
||||||
], $defaultConfig['values']);
|
'BLUE' => $enumValue,
|
||||||
|
],
|
||||||
|
$defaultConfig['values']
|
||||||
|
);
|
||||||
$this->assertCount(4, $defaultConfig); // 3 + astNode
|
$this->assertCount(4, $defaultConfig); // 3 + astNode
|
||||||
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
||||||
$this->assertEquals('My description of Color', $schema->getType('Color')->description);
|
$this->assertEquals('My description of Color', $schema->getType('Color')->description);
|
||||||
@ -1233,7 +1248,7 @@ interface Hello {
|
|||||||
|
|
||||||
public function testCreatesTypesLazily() : void
|
public function testCreatesTypesLazily() : void
|
||||||
{
|
{
|
||||||
$body = '
|
$body = '
|
||||||
schema {
|
schema {
|
||||||
query: Query
|
query: Query
|
||||||
}
|
}
|
||||||
@ -1258,11 +1273,12 @@ type World implements Hello {
|
|||||||
world: String
|
world: String
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
$created = [];
|
$created = [];
|
||||||
|
|
||||||
$typeConfigDecorator = function($config, $node) use (&$created) {
|
$typeConfigDecorator = function ($config, $node) use (&$created) {
|
||||||
$created[] = $node->name->value;
|
$created[] = $node->name->value;
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1282,5 +1298,4 @@ type World implements Hello {
|
|||||||
$this->assertArrayHasKey('Hello', $types);
|
$this->assertArrayHasKey('Hello', $types);
|
||||||
$this->assertArrayHasKey('World', $types);
|
$this->assertArrayHasKey('World', $types);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
|
||||||
use GraphQL\Executor\Values;
|
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
@ -12,13 +13,16 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class CoerceValueTest extends TestCase
|
class CoerceValueTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var EnumType */
|
||||||
private $testEnum;
|
private $testEnum;
|
||||||
|
|
||||||
|
/** @var InputObjectType */
|
||||||
private $testInputObject;
|
private $testInputObject;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->testEnum = new EnumType([
|
$this->testEnum = new EnumType([
|
||||||
'name' => 'TestEnum',
|
'name' => 'TestEnum',
|
||||||
'values' => [
|
'values' => [
|
||||||
'FOO' => 'InternalFoo',
|
'FOO' => 'InternalFoo',
|
||||||
'BAR' => 123456789,
|
'BAR' => 123456789,
|
||||||
@ -26,7 +30,7 @@ class CoerceValueTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->testInputObject = new InputObjectType([
|
$this->testInputObject = new InputObjectType([
|
||||||
'name' => 'TestInputObject',
|
'name' => 'TestInputObject',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'foo' => Type::nonNull(Type::int()),
|
'foo' => Type::nonNull(Type::int()),
|
||||||
'bar' => Type::int(),
|
'bar' => Type::int(),
|
||||||
@ -34,9 +38,9 @@ class CoerceValueTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: coerceValue
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Describe: coerceValue
|
||||||
|
*
|
||||||
* @see it('coercing an array to GraphQLString produces an error')
|
* @see it('coercing an array to GraphQLString produces an error')
|
||||||
*/
|
*/
|
||||||
public function testCoercingAnArrayToGraphQLStringProducesAnError() : void
|
public function testCoercingAnArrayToGraphQLStringProducesAnError() : void
|
||||||
@ -53,7 +57,17 @@ class CoerceValueTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: for GraphQLInt
|
/**
|
||||||
|
* Describe: for GraphQLInt
|
||||||
|
*/
|
||||||
|
private function expectError($result, $expected)
|
||||||
|
{
|
||||||
|
$this->assertInternalType('array', $result);
|
||||||
|
$this->assertInternalType('array', $result['errors']);
|
||||||
|
$this->assertCount(1, $result['errors']);
|
||||||
|
$this->assertEquals($expected, $result['errors'][0]->getMessage());
|
||||||
|
$this->assertEquals(Utils::undefined(), $result['value']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns no error for int input')
|
* @see it('returns no error for int input')
|
||||||
@ -64,6 +78,13 @@ class CoerceValueTest extends TestCase
|
|||||||
$this->expectNoErrors($result);
|
$this->expectNoErrors($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function expectNoErrors($result)
|
||||||
|
{
|
||||||
|
$this->assertInternalType('array', $result);
|
||||||
|
$this->assertNull($result['errors']);
|
||||||
|
$this->assertNotEquals(Utils::undefined(), $result['value']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns no error for negative int input')
|
* @see it('returns no error for negative int input')
|
||||||
*/
|
*/
|
||||||
@ -115,6 +136,8 @@ class CoerceValueTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Describe: for GraphQLFloat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns a single error for char input')
|
* @see it('returns a single error for char input')
|
||||||
*/
|
*/
|
||||||
@ -139,8 +162,6 @@ class CoerceValueTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe: for GraphQLFloat
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns no error for int input')
|
* @see it('returns no error for int input')
|
||||||
*/
|
*/
|
||||||
@ -189,6 +210,8 @@ class CoerceValueTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DESCRIBE: for GraphQLEnum
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns a single error for char input')
|
* @see it('returns a single error for char input')
|
||||||
*/
|
*/
|
||||||
@ -213,8 +236,6 @@ class CoerceValueTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DESCRIBE: for GraphQLEnum
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns no error for a known enum name')
|
* @see it('returns no error for a known enum name')
|
||||||
*/
|
*/
|
||||||
@ -229,6 +250,8 @@ class CoerceValueTest extends TestCase
|
|||||||
$this->assertEquals(123456789, $barResult['value']);
|
$this->assertEquals(123456789, $barResult['value']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DESCRIBE: for GraphQLInputObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('results error for misspelled enum value')
|
* @see it('results error for misspelled enum value')
|
||||||
*/
|
*/
|
||||||
@ -250,8 +273,6 @@ class CoerceValueTest extends TestCase
|
|||||||
$this->expectError($result2, 'Expected type TestEnum.');
|
$this->expectError($result2, 'Expected type TestEnum.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// DESCRIBE: for GraphQLInputObject
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('returns no error for a valid input')
|
* @see it('returns no error for a valid input')
|
||||||
*/
|
*/
|
||||||
@ -277,7 +298,10 @@ class CoerceValueTest extends TestCase
|
|||||||
public function testReturnErrorForAnInvalidField() : void
|
public function testReturnErrorForAnInvalidField() : void
|
||||||
{
|
{
|
||||||
$result = Value::coerceValue(['foo' => 'abc'], $this->testInputObject);
|
$result = Value::coerceValue(['foo' => 'abc'], $this->testInputObject);
|
||||||
$this->expectError($result, 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc');
|
$this->expectError(
|
||||||
|
$result,
|
||||||
|
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,10 +310,13 @@ class CoerceValueTest extends TestCase
|
|||||||
public function testReturnsMultipleErrorsForMultipleInvalidFields() : void
|
public function testReturnsMultipleErrorsForMultipleInvalidFields() : void
|
||||||
{
|
{
|
||||||
$result = Value::coerceValue(['foo' => 'abc', 'bar' => 'def'], $this->testInputObject);
|
$result = Value::coerceValue(['foo' => 'abc', 'bar' => 'def'], $this->testInputObject);
|
||||||
$this->assertEquals([
|
$this->assertEquals(
|
||||||
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc',
|
[
|
||||||
'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def',
|
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc',
|
||||||
], $result['errors']);
|
'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def',
|
||||||
|
],
|
||||||
|
$result['errors']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,20 +345,4 @@ class CoerceValueTest extends TestCase
|
|||||||
$result = Value::coerceValue(['foo' => 123, 'bart' => 123], $this->testInputObject);
|
$result = Value::coerceValue(['foo' => 123, 'bart' => 123], $this->testInputObject);
|
||||||
$this->expectError($result, 'Field "bart" is not defined by type TestInputObject; did you mean bar?');
|
$this->expectError($result, 'Field "bart" is not defined by type TestInputObject; did you mean bar?');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function expectNoErrors($result)
|
|
||||||
{
|
|
||||||
$this->assertInternalType('array', $result);
|
|
||||||
$this->assertNull($result['errors']);
|
|
||||||
$this->assertNotEquals(Utils::undefined(), $result['value']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private function expectError($result, $expected) {
|
|
||||||
$this->assertInternalType('array', $result);
|
|
||||||
$this->assertInternalType('array', $result['errors']);
|
|
||||||
$this->assertCount(1, $result['errors']);
|
|
||||||
$this->assertEquals($expected, $result['errors'][0]->getMessage());
|
|
||||||
$this->assertEquals(Utils::undefined(), $result['value']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Utils;
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
@ -12,121 +15,74 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class ExtractTypesTest extends TestCase
|
class ExtractTypesTest 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ObjectType
|
|
||||||
*/
|
|
||||||
private $videoMetadata;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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()
|
||||||
{
|
{
|
||||||
$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'),
|
||||||
@ -134,156 +90,176 @@ class ExtractTypesTest 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()
|
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->video = new ObjectType([
|
new ObjectType([
|
||||||
'name' => 'Video',
|
'name' => 'Link',
|
||||||
'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(),
|
'url' => Type::string(),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
new ObjectType([
|
||||||
|
'name' => 'Video',
|
||||||
|
'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'),
|
||||||
|
'streamUrl' => Type::string(),
|
||||||
'downloadUrl' => Type::string(),
|
'downloadUrl' => Type::string(),
|
||||||
'metadata' => $this->videoMetadata = new ObjectType([
|
'metadata' => new ObjectType([
|
||||||
'name' => 'VideoMetadata',
|
'name' => 'VideoMetadata',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'lat' => Type::float(),
|
'lat' => Type::float(),
|
||||||
'lng' => 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([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'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([
|
||||||
'name' => 'Mutation',
|
'name' => 'Mutation',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'postStory' => [
|
'postStory' => [
|
||||||
'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([
|
||||||
'name' => 'PostCommentMutationInput',
|
'name' => 'PostCommentMutationInput',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'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(),
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExtractTypesFromQuery() : void
|
public function testExtractTypesFromQuery() : void
|
||||||
{
|
{
|
||||||
$expectedTypeMap = [
|
$expectedTypeMap = [
|
||||||
'Query' => $this->query,
|
'Query' => $this->query,
|
||||||
'User' => $this->user,
|
'User' => $this->user,
|
||||||
'Node' => $this->node,
|
'Node' => $this->node,
|
||||||
'String' => Type::string(),
|
'String' => Type::string(),
|
||||||
'Content' => $this->content,
|
'Content' => $this->content,
|
||||||
'Comment' => $this->comment,
|
'Comment' => $this->comment,
|
||||||
'Mention' => $this->mention,
|
'Mention' => $this->mention,
|
||||||
'Category' => $this->category,
|
'Category' => $this->category,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -294,19 +270,19 @@ class ExtractTypesTest extends TestCase
|
|||||||
public function testExtractTypesFromMutation() : void
|
public function testExtractTypesFromMutation() : void
|
||||||
{
|
{
|
||||||
$expectedTypeMap = [
|
$expectedTypeMap = [
|
||||||
'Mutation' => $this->mutation,
|
'Mutation' => $this->mutation,
|
||||||
'User' => $this->user,
|
'User' => $this->user,
|
||||||
'Node' => $this->node,
|
'Node' => $this->node,
|
||||||
'String' => Type::string(),
|
'String' => Type::string(),
|
||||||
'Content' => $this->content,
|
'Content' => $this->content,
|
||||||
'Comment' => $this->comment,
|
'Comment' => $this->comment,
|
||||||
'BlogStory' => $this->blogStory,
|
'BlogStory' => $this->blogStory,
|
||||||
'Category' => $this->category,
|
'Category' => $this->category,
|
||||||
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
||||||
'ID' => Type::id(),
|
'ID' => Type::id(),
|
||||||
'PostStoryMutation' => $this->postStoryMutation,
|
'PostStoryMutation' => $this->postStoryMutation,
|
||||||
'PostCommentMutationInput' => $this->postCommentMutationInput,
|
'PostCommentMutationInput' => $this->postCommentMutationInput,
|
||||||
'PostCommentMutation' => $this->postCommentMutation,
|
'PostCommentMutation' => $this->postCommentMutation,
|
||||||
];
|
];
|
||||||
|
|
||||||
$actualTypeMap = TypeInfo::extractTypes($this->mutation);
|
$actualTypeMap = TypeInfo::extractTypes($this->mutation);
|
||||||
@ -316,16 +292,16 @@ class ExtractTypesTest extends TestCase
|
|||||||
public function testThrowsOnMultipleTypesWithSameName() : void
|
public function testThrowsOnMultipleTypesWithSameName() : void
|
||||||
{
|
{
|
||||||
$otherUserType = new ObjectType([
|
$otherUserType = new ObjectType([
|
||||||
'name' => 'User',
|
'name' => 'User',
|
||||||
'fields' => ['a' => Type::string()]
|
'fields' => ['a' => Type::string()],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$queryType = new ObjectType([
|
$queryType = new ObjectType([
|
||||||
'name' => 'Test',
|
'name' => 'Test',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'otherUser' => $otherUserType,
|
'otherUser' => $otherUserType,
|
||||||
'user' => $this->user
|
'user' => $this->user,
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->expectException(InvariantViolation::class);
|
$this->expectException(InvariantViolation::class);
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Language\SourceLocation;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
use GraphQL\Validator\DocumentValidator;
|
use GraphQL\Validator\DocumentValidator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class IsValidLiteralValueTest extends TestCase
|
class IsValidLiteralValueTest extends TestCase
|
||||||
{
|
{
|
||||||
// DESCRIBE: isValidLiteralValue
|
// DESCRIBE: isValidLiteralValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Returns no errors for a valid value')
|
* @see it('Returns no errors for a valid value')
|
||||||
*/
|
*/
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
|
|
||||||
use GraphQL\Utils\Utils;
|
|
||||||
use GraphQL\Utils\MixedStore;
|
use GraphQL\Utils\MixedStore;
|
||||||
|
use GraphQL\Utils\Utils;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class MixedStoreTest extends TestCase
|
class MixedStoreTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/** @var MixedStore */
|
||||||
* @var MixedStore
|
|
||||||
*/
|
|
||||||
private $mixedStore;
|
private $mixedStore;
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
@ -18,6 +18,13 @@ class MixedStoreTest extends TestCase
|
|||||||
$this->mixedStore = new MixedStore();
|
$this->mixedStore = new MixedStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAcceptsNullKeys() : void
|
||||||
|
{
|
||||||
|
foreach ($this->getPossibleValues() as $value) {
|
||||||
|
$this->assertAcceptsKeyValue(null, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getPossibleValues()
|
public function getPossibleValues()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -30,16 +37,38 @@ class MixedStoreTest extends TestCase
|
|||||||
'a',
|
'a',
|
||||||
[],
|
[],
|
||||||
new \stdClass(),
|
new \stdClass(),
|
||||||
function() {},
|
function () {
|
||||||
new MixedStore()
|
},
|
||||||
|
new MixedStore(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcceptsNullKeys() : void
|
private function assertAcceptsKeyValue($key, $value)
|
||||||
{
|
{
|
||||||
foreach ($this->getPossibleValues() as $value) {
|
$err = 'Failed assertion that MixedStore accepts key ' .
|
||||||
$this->assertAcceptsKeyValue(null, $value);
|
Utils::printSafe($key) . ' with value ' . Utils::printSafe($value);
|
||||||
}
|
|
||||||
|
$this->assertFalse($this->mixedStore->offsetExists($key), $err);
|
||||||
|
$this->mixedStore->offsetSet($key, $value);
|
||||||
|
$this->assertTrue($this->mixedStore->offsetExists($key), $err);
|
||||||
|
$this->assertSame($value, $this->mixedStore->offsetGet($key), $err);
|
||||||
|
$this->mixedStore->offsetUnset($key);
|
||||||
|
$this->assertFalse($this->mixedStore->offsetExists($key), $err);
|
||||||
|
$this->assertProvidesArrayAccess($key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertProvidesArrayAccess($key, $value)
|
||||||
|
{
|
||||||
|
$err = 'Failed assertion that MixedStore provides array access for key ' .
|
||||||
|
Utils::printSafe($key) . ' with value ' . Utils::printSafe($value);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mixedStore[$key]), $err);
|
||||||
|
$this->mixedStore[$key] = $value;
|
||||||
|
$this->assertTrue(isset($this->mixedStore[$key]), $err);
|
||||||
|
$this->assertEquals(! empty($value), ! empty($this->mixedStore[$key]), $err);
|
||||||
|
$this->assertSame($value, $this->mixedStore[$key], $err);
|
||||||
|
unset($this->mixedStore[$key]);
|
||||||
|
$this->assertFalse(isset($this->mixedStore[$key]), $err);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcceptsBoolKeys() : void
|
public function testAcceptsBoolKeys() : void
|
||||||
@ -93,35 +122,11 @@ class MixedStoreTest extends TestCase
|
|||||||
foreach ($this->getPossibleValues() as $value) {
|
foreach ($this->getPossibleValues() as $value) {
|
||||||
$this->assertAcceptsKeyValue(new \stdClass(), $value);
|
$this->assertAcceptsKeyValue(new \stdClass(), $value);
|
||||||
$this->assertAcceptsKeyValue(new MixedStore(), $value);
|
$this->assertAcceptsKeyValue(new MixedStore(), $value);
|
||||||
$this->assertAcceptsKeyValue(function() {}, $value);
|
$this->assertAcceptsKeyValue(
|
||||||
|
function () {
|
||||||
|
},
|
||||||
|
$value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function assertAcceptsKeyValue($key, $value)
|
|
||||||
{
|
|
||||||
$err = 'Failed assertion that MixedStore accepts key ' .
|
|
||||||
Utils::printSafe($key) . ' with value ' . Utils::printSafe($value);
|
|
||||||
|
|
||||||
$this->assertFalse($this->mixedStore->offsetExists($key), $err);
|
|
||||||
$this->mixedStore->offsetSet($key, $value);
|
|
||||||
$this->assertTrue($this->mixedStore->offsetExists($key), $err);
|
|
||||||
$this->assertSame($value, $this->mixedStore->offsetGet($key), $err);
|
|
||||||
$this->mixedStore->offsetUnset($key);
|
|
||||||
$this->assertFalse($this->mixedStore->offsetExists($key), $err);
|
|
||||||
$this->assertProvidesArrayAccess($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertProvidesArrayAccess($key, $value)
|
|
||||||
{
|
|
||||||
$err = 'Failed assertion that MixedStore provides array access for key ' .
|
|
||||||
Utils::printSafe($key) . ' with value ' . Utils::printSafe($value);
|
|
||||||
|
|
||||||
$this->assertFalse(isset($this->mixedStore[$key]), $err);
|
|
||||||
$this->mixedStore[$key] = $value;
|
|
||||||
$this->assertTrue(isset($this->mixedStore[$key]), $err);
|
|
||||||
$this->assertEquals(!empty($value), !empty($this->mixedStore[$key]), $err);
|
|
||||||
$this->assertSame($value, $this->mixedStore[$key], $err);
|
|
||||||
unset($this->mixedStore[$key]);
|
|
||||||
$this->assertFalse(isset($this->mixedStore[$key]), $err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Executor\Values;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use GraphQL\Utils\Value;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class QuotedOrListTest extends TestCase
|
class QuotedOrListTest extends TestCase
|
||||||
{
|
{
|
||||||
// DESCRIBE: quotedOrList
|
// DESCRIBE: quotedOrList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Does not accept an empty list')
|
* @see it('Does not accept an empty list')
|
||||||
*/
|
*/
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Language\DirectiveLocation;
|
use GraphQL\Language\DirectiveLocation;
|
||||||
use GraphQL\Type\Schema;
|
|
||||||
use GraphQL\Type\Definition\CustomScalarType;
|
use GraphQL\Type\Definition\CustomScalarType;
|
||||||
use GraphQL\Type\Definition\Directive;
|
use GraphQL\Type\Definition\Directive;
|
||||||
|
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;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
use GraphQL\Type\Definition\EnumType;
|
|
||||||
use GraphQL\Type\Definition\UnionType;
|
use GraphQL\Type\Definition\UnionType;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
use GraphQL\Utils\BuildSchema;
|
use GraphQL\Utils\BuildSchema;
|
||||||
use GraphQL\Utils\SchemaPrinter;
|
use GraphQL\Utils\SchemaPrinter;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -19,37 +22,40 @@ class SchemaPrinterTest extends TestCase
|
|||||||
{
|
{
|
||||||
// Describe: Type System Printer
|
// Describe: Type System Printer
|
||||||
|
|
||||||
private function printForTest($schema)
|
|
||||||
{
|
|
||||||
$schemaText = SchemaPrinter::doPrint($schema);
|
|
||||||
$this->assertEquals($schemaText, SchemaPrinter::doPrint(BuildSchema::build($schemaText)));
|
|
||||||
return "\n" . $schemaText;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function printSingleFieldSchema($fieldConfig)
|
|
||||||
{
|
|
||||||
$query = new ObjectType([
|
|
||||||
'name' => 'Query',
|
|
||||||
'fields' => [
|
|
||||||
'singleField' => $fieldConfig
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
return $this->printForTest(new Schema(['query' => $query]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Prints String Field')
|
* @see it('Prints String Field')
|
||||||
*/
|
*/
|
||||||
public function testPrintsStringField() : void
|
public function testPrintsStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string()
|
'type' => Type::string(),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: String
|
singleField: String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printSingleFieldSchema($fieldConfig)
|
||||||
|
{
|
||||||
|
$query = new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => ['singleField' => $fieldConfig],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->printForTest(new Schema(['query' => $query]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printForTest($schema)
|
||||||
|
{
|
||||||
|
$schemaText = SchemaPrinter::doPrint($schema);
|
||||||
|
$this->assertEquals($schemaText, SchemaPrinter::doPrint(BuildSchema::build($schemaText)));
|
||||||
|
|
||||||
|
return "\n" . $schemaText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,13 +64,16 @@ type Query {
|
|||||||
public function testPrintArrayStringField() : void
|
public function testPrintArrayStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::listOf(Type::string())
|
'type' => Type::listOf(Type::string()),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: [String]
|
singleField: [String]
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,13 +82,16 @@ type Query {
|
|||||||
public function testPrintNonNullStringField() : void
|
public function testPrintNonNullStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::nonNull(Type::string())
|
'type' => Type::nonNull(Type::string()),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: String!
|
singleField: String!
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,13 +100,16 @@ type Query {
|
|||||||
public function testPrintNonNullArrayStringField() : void
|
public function testPrintNonNullArrayStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::nonNull(Type::listOf(Type::string()))
|
'type' => Type::nonNull(Type::listOf(Type::string())),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: [String]!
|
singleField: [String]!
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,13 +118,16 @@ type Query {
|
|||||||
public function testPrintArrayNonNullStringField() : void
|
public function testPrintArrayNonNullStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::listOf(Type::nonNull(Type::string()))
|
'type' => Type::listOf(Type::nonNull(Type::string())),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: [String!]
|
singleField: [String!]
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,13 +136,16 @@ type Query {
|
|||||||
public function testPrintNonNullArrayNonNullStringField() : void
|
public function testPrintNonNullArrayNonNullStringField() : void
|
||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))
|
'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string()))),
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField: [String!]!
|
singleField: [String!]!
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,18 +154,19 @@ type Query {
|
|||||||
public function testPrintObjectField() : void
|
public function testPrintObjectField() : void
|
||||||
{
|
{
|
||||||
$fooType = new ObjectType([
|
$fooType = new ObjectType([
|
||||||
'name' => 'Foo',
|
'name' => 'Foo',
|
||||||
'fields' => ['str' => ['type' => Type::string()]]
|
'fields' => ['str' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$root = new ObjectType([
|
$root = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => ['foo' => ['type' => $fooType]]
|
'fields' => ['foo' => ['type' => $fooType]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $root]);
|
$schema = new Schema(['query' => $root]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Foo {
|
type Foo {
|
||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
@ -152,7 +174,9 @@ type Foo {
|
|||||||
type Query {
|
type Query {
|
||||||
foo: Foo
|
foo: Foo
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,13 +186,16 @@ type Query {
|
|||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => Type::int()]]
|
'args' => ['argOne' => ['type' => Type::int()]],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int): String
|
singleField(argOne: Int): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,13 +205,16 @@ type Query {
|
|||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => 2]]
|
'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => 2]],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int = 2): String
|
singleField(argOne: Int = 2): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,11 +226,14 @@ type Query {
|
|||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => Type::string(), 'defaultValue' => "tes\t de\fault"]],
|
'args' => ['argOne' => ['type' => Type::string(), 'defaultValue' => "tes\t de\fault"]],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: String = "tes\t de\fault"): String
|
singleField(argOne: String = "tes\t de\fault"): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -210,13 +243,16 @@ type Query {
|
|||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => null]]
|
'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => null]],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int = null): String
|
singleField(argOne: Int = null): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,13 +262,16 @@ type Query {
|
|||||||
{
|
{
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => Type::nonNull(Type::int())]]
|
'args' => ['argOne' => ['type' => Type::nonNull(Type::int())]],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int!): String
|
singleField(argOne: Int!): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,14 +283,17 @@ type Query {
|
|||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'argOne' => ['type' => Type::int()],
|
'argOne' => ['type' => Type::int()],
|
||||||
'argTwo' => ['type' => Type::string()]
|
'argTwo' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int, argTwo: String): String
|
singleField(argOne: Int, argTwo: String): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,16 +304,19 @@ type Query {
|
|||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'argOne' => ['type' => Type::int(), 'defaultValue' => 1],
|
'argOne' => ['type' => Type::int(), 'defaultValue' => 1],
|
||||||
'argTwo' => ['type' => Type::string()],
|
'argTwo' => ['type' => Type::string()],
|
||||||
'argThree' => ['type' => Type::boolean()]
|
'argThree' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String
|
singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -282,16 +327,19 @@ type Query {
|
|||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'argOne' => ['type' => Type::int()],
|
'argOne' => ['type' => Type::int()],
|
||||||
'argTwo' => ['type' => Type::string(), 'defaultValue' => 'foo'],
|
'argTwo' => ['type' => Type::string(), 'defaultValue' => 'foo'],
|
||||||
'argThree' => ['type' => Type::boolean()]
|
'argThree' => ['type' => Type::boolean()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String
|
singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,16 +350,19 @@ type Query {
|
|||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => [
|
'args' => [
|
||||||
'argOne' => ['type' => Type::int()],
|
'argOne' => ['type' => Type::int()],
|
||||||
'argTwo' => ['type' => Type::string()],
|
'argTwo' => ['type' => Type::string()],
|
||||||
'argThree' => ['type' => Type::boolean(), 'defaultValue' => false]
|
'argThree' => ['type' => Type::boolean(), 'defaultValue' => false],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String
|
singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -320,14 +371,12 @@ type Query {
|
|||||||
public function testPrintsCustomQueryRootType() : void
|
public function testPrintsCustomQueryRootType() : void
|
||||||
{
|
{
|
||||||
$customQueryType = new ObjectType([
|
$customQueryType = new ObjectType([
|
||||||
'name' => 'CustomQueryType',
|
'name' => 'CustomQueryType',
|
||||||
'fields' => ['bar' => ['type' => Type::string()]],
|
'fields' => ['bar' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema(['query' => $customQueryType]);
|
||||||
'query' => $customQueryType,
|
$output = $this->printForTest($schema);
|
||||||
]);
|
|
||||||
$output = $this->printForTest($schema);
|
|
||||||
$expected = '
|
$expected = '
|
||||||
schema {
|
schema {
|
||||||
query: CustomQueryType
|
query: CustomQueryType
|
||||||
@ -346,27 +395,28 @@ type CustomQueryType {
|
|||||||
public function testPrintInterface() : void
|
public function testPrintInterface() : void
|
||||||
{
|
{
|
||||||
$fooType = new InterfaceType([
|
$fooType = new InterfaceType([
|
||||||
'name' => 'Foo',
|
'name' => 'Foo',
|
||||||
'fields' => ['str' => ['type' => Type::string()]]
|
'fields' => ['str' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$barType = new ObjectType([
|
$barType = new ObjectType([
|
||||||
'name' => 'Bar',
|
'name' => 'Bar',
|
||||||
'fields' => ['str' => ['type' => Type::string()]],
|
'fields' => ['str' => ['type' => Type::string()]],
|
||||||
'interfaces' => [$fooType]
|
'interfaces' => [$fooType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => ['bar' => ['type' => $barType]]
|
'fields' => ['bar' => ['type' => $barType]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'types' => [$barType]
|
'types' => [$barType],
|
||||||
]);
|
]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Bar implements Foo {
|
type Bar implements Foo {
|
||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
@ -378,7 +428,9 @@ interface Foo {
|
|||||||
type Query {
|
type Query {
|
||||||
bar: Bar
|
bar: Bar
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,35 +439,36 @@ type Query {
|
|||||||
public function testPrintMultipleInterface() : void
|
public function testPrintMultipleInterface() : void
|
||||||
{
|
{
|
||||||
$fooType = new InterfaceType([
|
$fooType = new InterfaceType([
|
||||||
'name' => 'Foo',
|
'name' => 'Foo',
|
||||||
'fields' => ['str' => ['type' => Type::string()]]
|
'fields' => ['str' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$baazType = new InterfaceType([
|
$baazType = new InterfaceType([
|
||||||
'name' => 'Baaz',
|
'name' => 'Baaz',
|
||||||
'fields' => ['int' => ['type' => Type::int()]]
|
'fields' => ['int' => ['type' => Type::int()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$barType = new ObjectType([
|
$barType = new ObjectType([
|
||||||
'name' => 'Bar',
|
'name' => 'Bar',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'str' => ['type' => Type::string()],
|
'str' => ['type' => Type::string()],
|
||||||
'int' => ['type' => Type::int()]
|
'int' => ['type' => Type::int()],
|
||||||
],
|
],
|
||||||
'interfaces' => [$fooType, $baazType]
|
'interfaces' => [$fooType, $baazType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => ['bar' => ['type' => $barType]]
|
'fields' => ['bar' => ['type' => $barType]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'types' => [$barType]
|
'types' => [$barType],
|
||||||
]);
|
]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
interface Baaz {
|
interface Baaz {
|
||||||
int: Int
|
int: Int
|
||||||
}
|
}
|
||||||
@ -432,7 +485,9 @@ interface Foo {
|
|||||||
type Query {
|
type Query {
|
||||||
bar: Bar
|
bar: Bar
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -441,36 +496,37 @@ type Query {
|
|||||||
public function testPrintUnions() : void
|
public function testPrintUnions() : void
|
||||||
{
|
{
|
||||||
$fooType = new ObjectType([
|
$fooType = new ObjectType([
|
||||||
'name' => 'Foo',
|
'name' => 'Foo',
|
||||||
'fields' => ['bool' => ['type' => Type::boolean()]]
|
'fields' => ['bool' => ['type' => Type::boolean()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$barType = new ObjectType([
|
$barType = new ObjectType([
|
||||||
'name' => 'Bar',
|
'name' => 'Bar',
|
||||||
'fields' => ['str' => ['type' => Type::string()]]
|
'fields' => ['str' => ['type' => Type::string()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$singleUnion = new UnionType([
|
$singleUnion = new UnionType([
|
||||||
'name' => 'SingleUnion',
|
'name' => 'SingleUnion',
|
||||||
'types' => [$fooType]
|
'types' => [$fooType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$multipleUnion = new UnionType([
|
$multipleUnion = new UnionType([
|
||||||
'name' => 'MultipleUnion',
|
'name' => 'MultipleUnion',
|
||||||
'types' => [$fooType, $barType]
|
'types' => [$fooType, $barType],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'single' => ['type' => $singleUnion],
|
'single' => ['type' => $singleUnion],
|
||||||
'multiple' => ['type' => $multipleUnion]
|
'multiple' => ['type' => $multipleUnion],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Bar {
|
type Bar {
|
||||||
str: String
|
str: String
|
||||||
}
|
}
|
||||||
@ -487,7 +543,9 @@ type Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
union SingleUnion = Foo
|
union SingleUnion = Foo
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -496,23 +554,24 @@ union SingleUnion = Foo
|
|||||||
public function testInputType() : void
|
public function testInputType() : void
|
||||||
{
|
{
|
||||||
$inputType = new InputObjectType([
|
$inputType = new InputObjectType([
|
||||||
'name' => 'InputType',
|
'name' => 'InputType',
|
||||||
'fields' => ['int' => ['type' => Type::int()]]
|
'fields' => ['int' => ['type' => Type::int()]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'str' => [
|
'str' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'args' => ['argOne' => ['type' => $inputType]]
|
'args' => ['argOne' => ['type' => $inputType]],
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
input InputType {
|
input InputType {
|
||||||
int: Int
|
int: Int
|
||||||
}
|
}
|
||||||
@ -520,7 +579,9 @@ input InputType {
|
|||||||
type Query {
|
type Query {
|
||||||
str(argOne: InputType): String
|
str(argOne: InputType): String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -529,28 +590,31 @@ type Query {
|
|||||||
public function testCustomScalar() : void
|
public function testCustomScalar() : void
|
||||||
{
|
{
|
||||||
$oddType = new CustomScalarType([
|
$oddType = new CustomScalarType([
|
||||||
'name' => 'Odd',
|
'name' => 'Odd',
|
||||||
'serialize' => function($value) {
|
'serialize' => function ($value) {
|
||||||
return $value % 2 === 1 ? $value : null;
|
return $value % 2 === 1 ? $value : null;
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'odd' => ['type' => $oddType]
|
'odd' => ['type' => $oddType],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
scalar Odd
|
scalar Odd
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
odd: Odd
|
odd: Odd
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -559,24 +623,25 @@ type Query {
|
|||||||
public function testEnum() : void
|
public function testEnum() : void
|
||||||
{
|
{
|
||||||
$RGBType = new EnumType([
|
$RGBType = new EnumType([
|
||||||
'name' => 'RGB',
|
'name' => 'RGB',
|
||||||
'values' => [
|
'values' => [
|
||||||
'RED' => ['value' => 0],
|
'RED' => ['value' => 0],
|
||||||
'GREEN' => ['value' => 1],
|
'GREEN' => ['value' => 1],
|
||||||
'BLUE' => ['value' => 2]
|
'BLUE' => ['value' => 2],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'rgb' => ['type' => $RGBType]
|
'rgb' => ['type' => $RGBType],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
rgb: RGB
|
rgb: RGB
|
||||||
}
|
}
|
||||||
@ -586,7 +651,9 @@ enum RGB {
|
|||||||
GREEN
|
GREEN
|
||||||
BLUE
|
BLUE
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -595,32 +662,35 @@ enum RGB {
|
|||||||
public function testPrintsCustomDirectives() : void
|
public function testPrintsCustomDirectives() : void
|
||||||
{
|
{
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'field' => ['type' => Type::string()],
|
'field' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$customDirectives = new Directive([
|
$customDirectives = new Directive([
|
||||||
'name' => 'customDirective',
|
'name' => 'customDirective',
|
||||||
'locations' => [
|
'locations' => [
|
||||||
DirectiveLocation::FIELD
|
DirectiveLocation::FIELD,
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'directives' => [$customDirectives],
|
'directives' => [$customDirectives],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$output = $this->printForTest($schema);
|
$output = $this->printForTest($schema);
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
directive @customDirective on FIELD
|
directive @customDirective on FIELD
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
field: String
|
field: String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -629,19 +699,22 @@ type Query {
|
|||||||
public function testOneLinePrintsAShortDescription() : void
|
public function testOneLinePrintsAShortDescription() : void
|
||||||
{
|
{
|
||||||
$description = 'This field is awesome';
|
$description = 'This field is awesome';
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
"type" => Type::string(),
|
'type' => Type::string(),
|
||||||
"description" => $description
|
'description' => $description,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
"""This field is awesome"""
|
"""This field is awesome"""
|
||||||
singleField: String
|
singleField: String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
|
||||||
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
||||||
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
||||||
$this->assertEquals($description, $recreatedField->description);
|
$this->assertEquals($description, $recreatedField->description);
|
||||||
}
|
}
|
||||||
@ -652,21 +725,24 @@ type Query {
|
|||||||
public function testDoesNotOneLinePrintADescriptionThatEndsWithAQuote() : void
|
public function testDoesNotOneLinePrintADescriptionThatEndsWithAQuote() : void
|
||||||
{
|
{
|
||||||
$description = 'This field is "awesome"';
|
$description = 'This field is "awesome"';
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
"type" => Type::string(),
|
'type' => Type::string(),
|
||||||
"description" => $description
|
'description' => $description,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
"""
|
"""
|
||||||
This field is "awesome"
|
This field is "awesome"
|
||||||
"""
|
"""
|
||||||
singleField: String
|
singleField: String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
|
||||||
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
||||||
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
||||||
$this->assertEquals($description, $recreatedField->description);
|
$this->assertEquals($description, $recreatedField->description);
|
||||||
}
|
}
|
||||||
@ -677,20 +753,23 @@ type Query {
|
|||||||
public function testPReservesLeadingSpacesWhenPrintingADescription() : void
|
public function testPReservesLeadingSpacesWhenPrintingADescription() : void
|
||||||
{
|
{
|
||||||
$description = ' This field is "awesome"';
|
$description = ' This field is "awesome"';
|
||||||
$output = $this->printSingleFieldSchema([
|
$output = $this->printSingleFieldSchema([
|
||||||
"type" => Type::string(),
|
'type' => Type::string(),
|
||||||
"description" => $description
|
'description' => $description,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals('
|
$this->assertEquals(
|
||||||
|
'
|
||||||
type Query {
|
type Query {
|
||||||
""" This field is "awesome"
|
""" This field is "awesome"
|
||||||
"""
|
"""
|
||||||
singleField: String
|
singleField: String
|
||||||
}
|
}
|
||||||
', $output);
|
',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
|
||||||
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
$recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query'];
|
||||||
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
$recreatedField = $recreatedRoot->getFields()['singleField'];
|
||||||
$this->assertEquals($description, $recreatedField->description);
|
$this->assertEquals($description, $recreatedField->description);
|
||||||
}
|
}
|
||||||
@ -701,14 +780,14 @@ type Query {
|
|||||||
public function testPrintIntrospectionSchema() : void
|
public function testPrintIntrospectionSchema() : void
|
||||||
{
|
{
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'onlyField' => ['type' => Type::string()]
|
'onlyField' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = SchemaPrinter::printIntrosepctionSchema($schema);
|
$output = SchemaPrinter::printIntrosepctionSchema($schema);
|
||||||
$introspectionSchema = <<<'EOT'
|
$introspectionSchema = <<<'EOT'
|
||||||
"""
|
"""
|
||||||
Directs the executor to include this field or fragment only when the `if` argument is true.
|
Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||||
@ -946,16 +1025,17 @@ EOT;
|
|||||||
public function testPrintIntrospectionSchemaWithCommentDescriptions() : void
|
public function testPrintIntrospectionSchemaWithCommentDescriptions() : void
|
||||||
{
|
{
|
||||||
$query = new ObjectType([
|
$query = new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'onlyField' => ['type' => Type::string()]
|
'onlyField' => ['type' => Type::string()],
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema = new Schema(['query' => $query]);
|
$schema = new Schema(['query' => $query]);
|
||||||
$output = SchemaPrinter::printIntrosepctionSchema($schema, [
|
$output = SchemaPrinter::printIntrosepctionSchema(
|
||||||
'commentDescriptions' => true
|
$schema,
|
||||||
]);
|
['commentDescriptions' => true]
|
||||||
|
);
|
||||||
$introspectionSchema = <<<'EOT'
|
$introspectionSchema = <<<'EOT'
|
||||||
# Directs the executor to include this field or fragment only when the `if` argument is true.
|
# Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||||
directive @include(
|
directive @include(
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\Executor\Values;
|
|
||||||
use GraphQL\Type\Definition\Type;
|
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
use GraphQL\Utils\Value;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class SuggestionListTest extends TestCase
|
class SuggestionListTest extends TestCase
|
||||||
{
|
{
|
||||||
// DESCRIBE: suggestionList
|
// DESCRIBE: suggestionList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('Returns results when input is empty')
|
* @see it('Returns results when input is empty')
|
||||||
*/
|
*/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user