From 2ad79adf0c323a915c3a804792efdd10db46fe4e Mon Sep 17 00:00:00 2001 From: Jeremiah VALERIE Date: Sat, 26 Nov 2016 20:48:01 +0100 Subject: [PATCH] Add NonNull Promise tests --- tests/Executor/NonNullTest.php | 568 +++++++++++++++++++++++++++++++-- 1 file changed, 549 insertions(+), 19 deletions(-) diff --git a/tests/Executor/NonNullTest.php b/tests/Executor/NonNullTest.php index 6aa147f..976cf80 100644 --- a/tests/Executor/NonNullTest.php +++ b/tests/Executor/NonNullTest.php @@ -1,14 +1,17 @@ syncError = new \Exception('sync'); $this->nonNullSyncError = new \Exception('nonNullSync'); + $this->promiseError = new \Exception('promise'); + $this->nonNullPromiseError = new \Exception('nonNullPromise'); $this->throwingData = [ 'sync' => function () { @@ -33,12 +45,32 @@ class NonNullTest extends \PHPUnit_Framework_TestCase 'nonNullSync' => function () { throw $this->nonNullSyncError; }, + 'promise' => function () { + return new Promise(function () { + throw $this->promiseError; + }); + }, + 'nonNullPromise' => function () { + return new Promise(function () { + throw $this->nonNullPromiseError; + }); + }, 'nest' => function () { return $this->throwingData; }, 'nonNullNest' => function () { return $this->throwingData; }, + 'promiseNest' => function () { + return new Promise(function (callable $resolve) { + $resolve($this->throwingData); + }); + }, + 'nonNullPromiseNest' => function () { + return new Promise(function (callable $resolve) { + $resolve($this->throwingData); + }); + }, ]; $this->nullingData = [ @@ -48,12 +80,32 @@ class NonNullTest extends \PHPUnit_Framework_TestCase 'nonNullSync' => function () { return null; }, + 'promise' => function () { + return new Promise(function (callable $resolve) { + return $resolve(null); + }); + }, + 'nonNullPromise' => function () { + return new Promise(function (callable $resolve) { + return $resolve(null); + }); + }, 'nest' => function () { return $this->nullingData; }, 'nonNullNest' => function () { return $this->nullingData; }, + 'promiseNest' => function () { + return new Promise(function (callable $resolve) { + $resolve($this->nullingData); + }); + }, + 'nonNullPromiseNest' => function () { + return new Promise(function (callable $resolve) { + $resolve($this->nullingData); + }); + }, ]; $dataType = new ObjectType([ @@ -62,8 +114,12 @@ class NonNullTest extends \PHPUnit_Framework_TestCase return [ 'sync' => ['type' => Type::string()], 'nonNullSync' => ['type' => Type::nonNull(Type::string())], + 'promise' => Type::string(), + 'nonNullPromise' => Type::nonNull(Type::string()), 'nest' => $dataType, - 'nonNullNest' => Type::nonNull($dataType) + 'nonNullNest' => Type::nonNull($dataType), + 'promiseNest' => $dataType, + 'nonNullPromiseNest' => Type::nonNull($dataType), ]; } ]); @@ -71,6 +127,11 @@ class NonNullTest extends \PHPUnit_Framework_TestCase $this->schema = new Schema(['query' => $dataType]); } + public function tearDown() + { + Executor::setPromiseAdapter(null); + } + // Execute: handles non-nullable types /** @@ -100,6 +161,32 @@ class NonNullTest extends \PHPUnit_Framework_TestCase $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); } + public function testNullsANullableFieldThatThrowsInAPromise() + { + $doc = ' + query Q { + promise + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promise' => null, + ], + 'errors' => [ + FormattedError::create( + $this->promiseError->getMessage(), + [new SourceLocation(3, 9)] + ) + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); + } + public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() { // nulls a synchronously returned object that contains a non-nullable field that throws synchronously @@ -124,18 +211,111 @@ class NonNullTest extends \PHPUnit_Framework_TestCase $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); } + public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() + { + $doc = ' + query Q { + nest { + nonNullPromise, + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'nest' => null + ], + 'errors' => [ + FormattedError::create($this->nonNullPromiseError->getMessage(), [new SourceLocation(4, 11)]) + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); + } + + public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() + { + $doc = ' + query Q { + promiseNest { + nonNullSync, + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promiseNest' => null + ], + 'errors' => [ + FormattedError::create($this->nonNullSyncError->getMessage(), [new SourceLocation(4, 11)]) + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); + } + + public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() + { + $doc = ' + query Q { + promiseNest { + nonNullPromise, + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promiseNest' => null + ], + 'errors' => [ + FormattedError::create($this->nonNullPromiseError->getMessage(), [new SourceLocation(4, 11)]) + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); + } + public function testNullsAComplexTreeOfNullableFieldsThatThrow() { $doc = ' query Q { nest { sync + promise nest { sync + promise + } + promiseNest { + sync + promise + } + } + promiseNest { + sync + promise + nest { + sync + promise + } + promiseNest { + sync + promise } } } - '; + '; $ast = Parser::parse($doc); @@ -143,17 +323,119 @@ class NonNullTest extends \PHPUnit_Framework_TestCase 'data' => [ 'nest' => [ 'sync' => null, + 'promise' => null, 'nest' => [ 'sync' => null, - ] - ] + 'promise' => null, + ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + ], + ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + 'nest' => [ + 'sync' => null, + 'promise' => null, + ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + ], + ], ], 'errors' => [ FormattedError::create($this->syncError->getMessage(), [new SourceLocation(4, 11)]), - FormattedError::create($this->syncError->getMessage(), [new SourceLocation(6, 13)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(5, 11)]), + FormattedError::create($this->syncError->getMessage(), [new SourceLocation(7, 13)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(8, 13)]), + FormattedError::create($this->syncError->getMessage(), [new SourceLocation(11, 13)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(12, 13)]), + FormattedError::create($this->syncError->getMessage(), [new SourceLocation(16, 11)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]), + FormattedError::create($this->syncError->getMessage(), [new SourceLocation(19, 13)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]), + FormattedError::create($this->syncError->getMessage(), [new SourceLocation(23, 13)]), + FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]), ] ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); + } + + public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() + { + $doc = ' + query Q { + nest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullSync + } + } + } + } + } + promiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullSync + } + } + } + } + } + anotherNest: nest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullPromise + } + } + } + } + } + anotherPromiseNest: promiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullPromise + } + } + } + } + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'nest' => null, + 'promiseNest' => null, + 'anotherNest' => null, + 'anotherPromiseNest' => null, + ], + 'errors' => [ + FormattedError::create($this->nonNullSyncError->getMessage(), [new SourceLocation(8, 19)]), + FormattedError::create($this->nonNullSyncError->getMessage(), [new SourceLocation(19, 19)]), + FormattedError::create($this->nonNullPromiseError->getMessage(), [new SourceLocation(30, 19)]), + FormattedError::create($this->nonNullPromiseError->getMessage(), [new SourceLocation(41, 19)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); } public function testNullsANullableFieldThatSynchronouslyReturnsNull() @@ -174,9 +456,28 @@ class NonNullTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); } - public function test4() + public function testNullsANullableFieldThatReturnsNullInAPromise() + { + $doc = ' + query Q { + promise + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promise' => null, + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); + } + + public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() { - // nulls a synchronously returned object that contains a non-nullable field that returns null synchronously $doc = ' query Q { nest { @@ -198,19 +499,107 @@ class NonNullTest extends \PHPUnit_Framework_TestCase $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); } - public function test5() + public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() { - // nulls a complex tree of nullable fields that return null + $doc = ' + query Q { + nest { + nonNullPromise, + } + } + '; + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'nest' => null, + ], + 'errors' => [ + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(4, 11)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); + } + + public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullSynchronously() + { + $doc = ' + query Q { + promiseNest { + nonNullSync, + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promiseNest' => null, + ], + 'errors' => [ + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(4, 11)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); + } + + public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullInaAPromise() + { + $doc = ' + query Q { + promiseNest { + nonNullPromise, + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'promiseNest' => null, + ], + 'errors' => [ + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(4, 11)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); + } + + public function testNullsAComplexTreeOfNullableFieldsThatReturnNull() + { $doc = ' query Q { nest { sync + promise nest { sync - nest { - sync - } + promise + } + promiseNest { + sync + promise + } + } + promiseNest { + sync + promise + nest { + sync + promise + } + promiseNest { + sync + promise } } } @@ -222,16 +611,107 @@ class NonNullTest extends \PHPUnit_Framework_TestCase 'data' => [ 'nest' => [ 'sync' => null, + 'promise' => null, 'nest' => [ 'sync' => null, - 'nest' => [ - 'sync' => null - ] + 'promise' => null, ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + ] ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + 'nest' => [ + 'sync' => null, + 'promise' => null, + ], + 'promiseNest' => [ + 'sync' => null, + 'promise' => null, + ] + ] ] ]; - $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->then(function ($actual) use ($expected) { + $this->assertEquals($expected, $actual); + }); + } + + public function testNullsTheFirstNullableObjectAfterAFieldReturnsNullInALongChainOfFieldsThatAreNonNull() + { + $doc = ' + query Q { + nest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullSync + } + } + } + } + } + promiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullSync + } + } + } + } + } + anotherNest: nest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullPromise + } + } + } + } + } + anotherPromiseNest: promiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullNest { + nonNullPromiseNest { + nonNullPromise + } + } + } + } + } + } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => [ + 'nest' => null, + 'promiseNest' => null, + 'anotherNest' => null, + 'anotherPromiseNest' => null, + ], + 'errors' => [ + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(8, 19)]), + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(19, 19)]), + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(30, 19)]), + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(41, 19)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); } public function testNullsTheTopLevelIfSyncNonNullableFieldThrows() @@ -241,11 +721,32 @@ class NonNullTest extends \PHPUnit_Framework_TestCase '; $expected = [ + 'data' => null, 'errors' => [ FormattedError::create($this->nonNullSyncError->getMessage(), [new SourceLocation(2, 17)]) ] ]; - $this->assertArraySubset($expected, 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); + } + + public function testNullsTheTopLevelIfAsyncNonNullableFieldErrors() + { + $doc = ' + query Q { nonNullPromise } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => null, + 'errors' => [ + FormattedError::create($this->nonNullPromiseError->getMessage(), [new SourceLocation(2, 17)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')); } public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() @@ -262,4 +763,33 @@ class NonNullTest extends \PHPUnit_Framework_TestCase ]; $this->assertArraySubset($expected, Executor::execute($this->schema, Parser::parse($doc), $this->nullingData)->toArray()); } + + public function testNullsTheTopLevelIfAsyncNonNullableFieldResolvesNull() + { + $doc = ' + query Q { nonNullPromise } + '; + + $ast = Parser::parse($doc); + + $expected = [ + 'data' => null, + 'errors' => [ + FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(2, 17)]), + ] + ]; + + Executor::setPromiseAdapter(new ReactPromiseAdapter()); + $this->assertArraySubsetPromise($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')); + } + + private function assertArraySubsetPromise($subset, PromiseInterface $promise) + { + $array = null; + $promise->then(function ($value) use (&$array) { + $array = $value->toArray(); + }); + + $this->assertArraySubset($subset, $array); + } }