From 8d2dcb09577b0635a8b8ac81aacd6a75756a24e8 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 17 Aug 2026 15:31:09 +0200 Subject: [PATCH 1/3] test: run shared suites with each sync adapter Keep the shared DataLoader behavior tests in one abstract suite and run them through explicit React and Webonyx test classes. Reset retained DataLoader instances between cases so adapter-specific state cannot leak across the matrix. --- tests/{AbuseTest.php => AbuseTestCase.php} | 7 +++--- ...{DataLoadTest.php => DataLoadTestCase.php} | 7 +++--- tests/ReactAbuseTest.php | 23 +++++++++++++++++++ tests/ReactDataLoadTest.php | 23 +++++++++++++++++++ tests/TestCase.php | 14 +++++++++-- tests/WebonyxAbuseTest.php | 23 +++++++++++++++++++ tests/WebonyxDataLoadTest.php | 23 +++++++++++++++++++ 7 files changed, 111 insertions(+), 9 deletions(-) rename tests/{AbuseTest.php => AbuseTestCase.php} (94%) rename tests/{DataLoadTest.php => DataLoadTestCase.php} (99%) create mode 100644 tests/ReactAbuseTest.php create mode 100644 tests/ReactDataLoadTest.php create mode 100644 tests/WebonyxAbuseTest.php create mode 100644 tests/WebonyxDataLoadTest.php diff --git a/tests/AbuseTest.php b/tests/AbuseTestCase.php similarity index 94% rename from tests/AbuseTest.php rename to tests/AbuseTestCase.php index c163fa8..dadd0a0 100644 --- a/tests/AbuseTest.php +++ b/tests/AbuseTestCase.php @@ -13,10 +13,9 @@ use InvalidArgumentException; use Overblog\DataLoader\DataLoader; -use React\Promise\Promise; use RuntimeException; -class AbuseTest extends TestCase +abstract class AbuseTestCase extends TestCase { /** * @group provides-descriptive-error-messages-for-api-abuse @@ -34,7 +33,7 @@ public function testLoadFunctionRequiresAKeyNotNull() */ public function testLoadFunctionRequiresAKeyWith0() { - self::assertInstanceOf(Promise::class, self::idLoader()->load(0)); + self::assertTrue(self::$promiseAdapter->isPromise(self::idLoader()->load(0), true)); } /** @@ -53,7 +52,7 @@ public function testLoadManyFunctionRequiresAListOfKey() */ public function testLoadManyFunctionRequiresAListEmptyArrayAccepted() { - self::assertInstanceOf(Promise::class, self::idLoader()->loadMany([])); + self::assertTrue(self::$promiseAdapter->isPromise(self::idLoader()->loadMany([]), true)); } /** diff --git a/tests/DataLoadTest.php b/tests/DataLoadTestCase.php similarity index 99% rename from tests/DataLoadTest.php rename to tests/DataLoadTestCase.php index fd17e01..ae2a4ae 100644 --- a/tests/DataLoadTest.php +++ b/tests/DataLoadTestCase.php @@ -15,7 +15,7 @@ use Overblog\DataLoader\DataLoader; use Overblog\DataLoader\Option; -class DataLoadTest extends TestCase +abstract class DataLoadTestCase extends TestCase { /** * @group primary-api @@ -306,7 +306,7 @@ public function testCanRepresentFailuresAndSuccessesSimultaneously() $promise1->then(null, function ($error) use (&$caughtError) { $caughtError = $error; }); - DataLoader::await(); + DataLoader::await($promise1, false); $this->assertInstanceOf(\Exception::class, $caughtError); $this->assertEquals($caughtError->getMessage(), 'Odd: 1'); @@ -914,6 +914,7 @@ private static function idLoader(?Option $options = null, ?callable $batchLoadFn private function assertInstanceOfPromise($object) { - $this->assertTrue(self::$promiseAdapter->isPromise($object, true)); + $adapter = self::$promiseAdapter; + $this->assertTrue($adapter->isPromise($object, true)); } } diff --git a/tests/ReactAbuseTest.php b/tests/ReactAbuseTest.php new file mode 100644 index 0000000..d85f7b1 --- /dev/null +++ b/tests/ReactAbuseTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Overblog\DataLoader\Test; + +use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; +use Overblog\PromiseAdapter\PromiseAdapterInterface; + +class ReactAbuseTest extends AbuseTestCase +{ + protected function createPromiseAdapter(): PromiseAdapterInterface + { + return new ReactPromiseAdapter(); + } +} diff --git a/tests/ReactDataLoadTest.php b/tests/ReactDataLoadTest.php new file mode 100644 index 0000000..effb723 --- /dev/null +++ b/tests/ReactDataLoadTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Overblog\DataLoader\Test; + +use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; +use Overblog\PromiseAdapter\PromiseAdapterInterface; + +class ReactDataLoadTest extends DataLoadTestCase +{ + protected function createPromiseAdapter(): PromiseAdapterInterface + { + return new ReactPromiseAdapter(); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 755ef26..afea948 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -11,7 +11,7 @@ namespace Overblog\DataLoader\Test; -use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; +use Overblog\DataLoader\DataLoader; use Overblog\PromiseAdapter\PromiseAdapterInterface; abstract class TestCase extends \PHPUnit\Framework\TestCase @@ -23,6 +23,16 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase public function setUp(): void { - self::$promiseAdapter = new ReactPromiseAdapter(); + self::$promiseAdapter = $this->createPromiseAdapter(); } + + protected function tearDown(): void + { + $instances = new \ReflectionProperty(DataLoader::class, 'instances'); + $instances->setValue([]); + + parent::tearDown(); + } + + abstract protected function createPromiseAdapter(): PromiseAdapterInterface; } diff --git a/tests/WebonyxAbuseTest.php b/tests/WebonyxAbuseTest.php new file mode 100644 index 0000000..229ccb6 --- /dev/null +++ b/tests/WebonyxAbuseTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Overblog\DataLoader\Test; + +use Overblog\PromiseAdapter\Adapter\WebonyxGraphQLSyncPromiseAdapter; +use Overblog\PromiseAdapter\PromiseAdapterInterface; + +class WebonyxAbuseTest extends AbuseTestCase +{ + protected function createPromiseAdapter(): PromiseAdapterInterface + { + return new WebonyxGraphQLSyncPromiseAdapter(); + } +} diff --git a/tests/WebonyxDataLoadTest.php b/tests/WebonyxDataLoadTest.php new file mode 100644 index 0000000..e27d4cc --- /dev/null +++ b/tests/WebonyxDataLoadTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Overblog\DataLoader\Test; + +use Overblog\PromiseAdapter\Adapter\WebonyxGraphQLSyncPromiseAdapter; +use Overblog\PromiseAdapter\PromiseAdapterInterface; + +class WebonyxDataLoadTest extends DataLoadTestCase +{ + protected function createPromiseAdapter(): PromiseAdapterInterface + { + return new WebonyxGraphQLSyncPromiseAdapter(); + } +} From 7404ee38ebc90e340b7265ba06505b6e692356bf Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 17 Aug 2026 21:19:31 +0200 Subject: [PATCH 2/3] test: make adapter suites final --- tests/ReactAbuseTest.php | 2 +- tests/ReactDataLoadTest.php | 2 +- tests/WebonyxAbuseTest.php | 2 +- tests/WebonyxDataLoadTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ReactAbuseTest.php b/tests/ReactAbuseTest.php index d85f7b1..1346cd9 100644 --- a/tests/ReactAbuseTest.php +++ b/tests/ReactAbuseTest.php @@ -14,7 +14,7 @@ use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; use Overblog\PromiseAdapter\PromiseAdapterInterface; -class ReactAbuseTest extends AbuseTestCase +final class ReactAbuseTest extends AbuseTestCase { protected function createPromiseAdapter(): PromiseAdapterInterface { diff --git a/tests/ReactDataLoadTest.php b/tests/ReactDataLoadTest.php index effb723..76528ac 100644 --- a/tests/ReactDataLoadTest.php +++ b/tests/ReactDataLoadTest.php @@ -14,7 +14,7 @@ use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; use Overblog\PromiseAdapter\PromiseAdapterInterface; -class ReactDataLoadTest extends DataLoadTestCase +final class ReactDataLoadTest extends DataLoadTestCase { protected function createPromiseAdapter(): PromiseAdapterInterface { diff --git a/tests/WebonyxAbuseTest.php b/tests/WebonyxAbuseTest.php index 229ccb6..e248b4a 100644 --- a/tests/WebonyxAbuseTest.php +++ b/tests/WebonyxAbuseTest.php @@ -14,7 +14,7 @@ use Overblog\PromiseAdapter\Adapter\WebonyxGraphQLSyncPromiseAdapter; use Overblog\PromiseAdapter\PromiseAdapterInterface; -class WebonyxAbuseTest extends AbuseTestCase +final class WebonyxAbuseTest extends AbuseTestCase { protected function createPromiseAdapter(): PromiseAdapterInterface { diff --git a/tests/WebonyxDataLoadTest.php b/tests/WebonyxDataLoadTest.php index e27d4cc..98831a1 100644 --- a/tests/WebonyxDataLoadTest.php +++ b/tests/WebonyxDataLoadTest.php @@ -14,7 +14,7 @@ use Overblog\PromiseAdapter\Adapter\WebonyxGraphQLSyncPromiseAdapter; use Overblog\PromiseAdapter\PromiseAdapterInterface; -class WebonyxDataLoadTest extends DataLoadTestCase +final class WebonyxDataLoadTest extends DataLoadTestCase { protected function createPromiseAdapter(): PromiseAdapterInterface { From 8107b6aa9a38a53dc4ac223a77c27e7ffa020de2 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 17 Aug 2026 21:23:32 +0200 Subject: [PATCH 3/3] test: keep loader-free await tests on React --- tests/DataLoadTestCase.php | 44 ------------------------------------ tests/ReactDataLoadTest.php | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/tests/DataLoadTestCase.php b/tests/DataLoadTestCase.php index ae2a4ae..74849f0 100644 --- a/tests/DataLoadTestCase.php +++ b/tests/DataLoadTestCase.php @@ -815,50 +815,6 @@ public function testAwaitAlsoAwaitsNewlyCreatedDataloaders() $this->assertTrue($secondComplete); } - /** - * @runInSeparateProcess - */ - public function testAwaitShouldReturnTheValueOfFulfilledPromiseWithoutNeedingActiveDataLoaderInstance() - { - $expectedValue = 'Ok!'; - $value = DataLoader::await(self::$promiseAdapter->createFulfilled($expectedValue)); - - $this->assertEquals($expectedValue, $value); - } - - /** - * @runInSeparateProcess - */ - public function testAwaitShouldReturnTheRejectReasonOfRejectedPromiseWithoutNeedingActiveDataLoaderInstance() - { - $expectedException = new \Exception('Rejected!'); - $exception = DataLoader::await(self::$promiseAdapter->createRejected($expectedException), false); - - $this->assertEquals($expectedException, $exception); - } - - /** - * @runInSeparateProcess - */ - public function testAwaitShouldThrowTheRejectReasonOfRejectedPromiseWithoutNeedingActiveDataLoaderInstance() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Rejected!'); - - DataLoader::await(self::$promiseAdapter->createRejected(new Exception('Rejected!'))); - } - - /** - * @runInSeparateProcess - */ - public function testAwaitShouldThrowThrowable() - { - $this->expectException(\Error::class); - $this->expectExceptionMessage('Rejected Error!'); - - DataLoader::await(self::$promiseAdapter->createRejected(new \Error('Rejected Error!'))); - } - public function cacheKey($key) { $cacheKey = []; diff --git a/tests/ReactDataLoadTest.php b/tests/ReactDataLoadTest.php index 76528ac..727529d 100644 --- a/tests/ReactDataLoadTest.php +++ b/tests/ReactDataLoadTest.php @@ -11,6 +11,7 @@ namespace Overblog\DataLoader\Test; +use Overblog\DataLoader\DataLoader; use Overblog\PromiseAdapter\Adapter\ReactPromiseAdapter; use Overblog\PromiseAdapter\PromiseAdapterInterface; @@ -20,4 +21,48 @@ protected function createPromiseAdapter(): PromiseAdapterInterface { return new ReactPromiseAdapter(); } + + /** + * @runInSeparateProcess + */ + public function testAwaitShouldReturnTheValueOfFulfilledPromiseWithoutNeedingActiveDataLoaderInstance() + { + $expectedValue = 'Ok!'; + $value = DataLoader::await(self::$promiseAdapter->createFulfilled($expectedValue)); + + $this->assertEquals($expectedValue, $value); + } + + /** + * @runInSeparateProcess + */ + public function testAwaitShouldReturnTheRejectReasonOfRejectedPromiseWithoutNeedingActiveDataLoaderInstance() + { + $expectedException = new \Exception('Rejected!'); + $exception = DataLoader::await(self::$promiseAdapter->createRejected($expectedException), false); + + $this->assertEquals($expectedException, $exception); + } + + /** + * @runInSeparateProcess + */ + public function testAwaitShouldThrowTheRejectReasonOfRejectedPromiseWithoutNeedingActiveDataLoaderInstance() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Rejected!'); + + DataLoader::await(self::$promiseAdapter->createRejected(new \Exception('Rejected!'))); + } + + /** + * @runInSeparateProcess + */ + public function testAwaitShouldThrowThrowable() + { + $this->expectException(\Error::class); + $this->expectExceptionMessage('Rejected Error!'); + + DataLoader::await(self::$promiseAdapter->createRejected(new \Error('Rejected Error!'))); + } }