graphql-php/tests/Executor/MutationsTest.php

257 lines
7.4 KiB
PHP
Raw Normal View History

2015-07-15 20:05:46 +03:00
<?php
2016-04-09 10:36:53 +03:00
namespace GraphQL\Tests\Executor;
2015-07-15 20:05:46 +03:00
2016-11-26 22:48:49 +03:00
use GraphQL\Executor\ExecutionResult;
2016-04-09 10:36:53 +03:00
use GraphQL\Executor\Executor;
use GraphQL\Error\FormattedError;
2016-11-26 22:48:49 +03:00
use GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter;
2015-07-15 20:05:46 +03:00
use GraphQL\Language\Parser;
use GraphQL\Language\SourceLocation;
use GraphQL\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
2016-11-26 22:48:49 +03:00
use React\Promise\Promise;
2015-07-15 20:05:46 +03:00
class MutationsTest extends \PHPUnit_Framework_TestCase
{
2016-11-26 22:48:49 +03:00
public static function setUpBeforeClass()
{
Executor::setPromiseAdapter(new ReactPromiseAdapter());
}
public static function tearDownAfterClass()
{
Executor::setPromiseAdapter(null);
}
2015-07-15 20:05:46 +03:00
// Execute: Handles mutation execution ordering
/**
* @it evaluates mutations serially
*/
2015-07-15 20:05:46 +03:00
public function testEvaluatesMutationsSerially()
{
$doc = 'mutation M {
first: immediatelyChangeTheNumber(newNumber: 1) {
theNumber
},
second: promiseToChangeTheNumber(newNumber: 2) {
theNumber
},
third: immediatelyChangeTheNumber(newNumber: 3) {
theNumber
}
fourth: promiseToChangeTheNumber(newNumber: 4) {
theNumber
},
fifth: immediatelyChangeTheNumber(newNumber: 5) {
theNumber
}
}';
$ast = Parser::parse($doc);
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
2015-07-15 20:05:46 +03:00
$expected = [
'data' => [
'first' => [
'theNumber' => 1
],
'second' => [
'theNumber' => 2
],
'third' => [
'theNumber' => 3
],
'fourth' => [
'theNumber' => 4
],
'fifth' => [
'theNumber' => 5
]
]
];
2016-11-26 22:48:49 +03:00
$this->assertEquals($expected, self::awaitPromise($mutationResult));
2015-07-15 20:05:46 +03:00
}
/**
* @it evaluates mutations correctly in the presense of a failed mutation
*/
2015-07-15 20:05:46 +03:00
public function testEvaluatesMutationsCorrectlyInThePresenseOfAFailedMutation()
{
$doc = 'mutation M {
first: immediatelyChangeTheNumber(newNumber: 1) {
theNumber
},
second: promiseToChangeTheNumber(newNumber: 2) {
theNumber
},
third: failToChangeTheNumber(newNumber: 3) {
theNumber
}
fourth: promiseToChangeTheNumber(newNumber: 4) {
theNumber
},
fifth: immediatelyChangeTheNumber(newNumber: 5) {
theNumber
}
sixth: promiseAndFailToChangeTheNumber(newNumber: 6) {
theNumber
}
}';
$ast = Parser::parse($doc);
$mutationResult = Executor::execute($this->schema(), $ast, new Root(6));
2015-07-15 20:05:46 +03:00
$expected = [
'data' => [
'first' => [
'theNumber' => 1
],
'second' => [
'theNumber' => 2
],
'third' => null,
'fourth' => [
'theNumber' => 4
],
'fifth' => [
'theNumber' => 5
],
'sixth' => null,
],
'errors' => [
FormattedError::create(
2015-07-15 20:05:46 +03:00
'Cannot change the number',
[new SourceLocation(8, 7)]
),
FormattedError::create(
2015-07-15 20:05:46 +03:00
'Cannot change the number',
[new SourceLocation(17, 7)]
)
]
];
2016-11-26 22:48:49 +03:00
$this->assertArraySubset($expected, self::awaitPromise($mutationResult));
}
/**
* @param \GraphQL\Executor\Promise\Promise $promise
* @return array
*/
private static function awaitPromise($promise)
{
$results = null;
$promise->then(function (ExecutionResult $executionResult) use (&$results) {
$results = $executionResult->toArray();
});
return $results;
2015-07-15 20:05:46 +03:00
}
private function schema()
{
$numberHolderType = new ObjectType([
'fields' => [
'theNumber' => ['type' => Type::int()],
],
'name' => 'NumberHolder',
]);
$schema = new Schema([
'query' => new ObjectType([
2015-07-15 20:05:46 +03:00
'fields' => [
'numberHolder' => ['type' => $numberHolderType],
],
'name' => 'Query',
]),
'mutation' => new ObjectType([
2015-07-15 20:05:46 +03:00
'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',
])
]);
2015-07-15 20:05:46 +03:00
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
2016-11-26 22:48:49 +03:00
*
* @return Promise
2015-07-15 20:05:46 +03:00
*/
public function promiseToChangeTheNumber($newNumber)
{
2016-11-26 22:48:49 +03:00
return new Promise(function (callable $resolve) use ($newNumber) {
return $resolve($this->immediatelyChangeTheNumber($newNumber));
});
2015-07-15 20:05:46 +03:00
}
/**
* @throws \Exception
*/
public function failToChangeTheNumber()
{
throw new \Exception('Cannot change the number');
}
/**
2016-11-26 22:48:49 +03:00
* @return Promise
2015-07-15 20:05:46 +03:00
*/
public function promiseAndFailToChangeTheNumber()
{
2016-11-26 22:48:49 +03:00
return new Promise(function (callable $resolve, callable $reject) {
return $reject(new \Exception("Cannot change the number"));
});
2015-07-15 20:05:46 +03:00
}
}