diff --git a/src/Schema/Storage/AbstractTypeRegistry.php b/src/Schema/Storage/AbstractTypeRegistry.php index a34e3c1a3..5b305f4b7 100644 --- a/src/Schema/Storage/AbstractTypeRegistry.php +++ b/src/Schema/Storage/AbstractTypeRegistry.php @@ -47,7 +47,7 @@ public static function get(string $typename) try { return static::fromCache($typename); } catch (Exception $e) { - if (!preg_match('/(Missing|Unknown) graphql/', $e->getMessage()) || !AbstractTypeRegistry::canRebuildOnMissing()) { + if (!preg_match('/(Missing|Unknown) graphql/', $e->getMessage()) || !static::canRebuildOnMissing()) { throw $e; } // Try to rebuild the whole schema as fallback. @@ -59,7 +59,7 @@ public static function get(string $typename) $schema = $builder->boot($key); try { $builder->build($schema, true); - $path = AbstractTypeRegistry::getRebuildOnMissingPath(); + $path = static::getRebuildOnMissingPath(); file_put_contents($path, time()); } catch (EmptySchemaException $e) { // noop @@ -71,9 +71,10 @@ public static function get(string $typename) private static function canRebuildOnMissing(): bool { - if (!Controller::has_curr() || - !(Controller::curr() instanceof GraphQLController) || - !Controller::curr()->autobuildEnabled() + $controller = Controller::curr(); + if (!$controller || + !($controller instanceof GraphQLController) || + !$controller->autobuildEnabled() ) { return false; } diff --git a/tests/Schema/AbstractTypeRegistryTest.php b/tests/Schema/AbstractTypeRegistryTest.php index 556100f25..b71e46a83 100644 --- a/tests/Schema/AbstractTypeRegistryTest.php +++ b/tests/Schema/AbstractTypeRegistryTest.php @@ -2,16 +2,16 @@ namespace SilverStripe\GraphQL\Tests\Schema; -use GraphQL\Type\Definition\AbstractType; +use Exception; use SilverStripe\Control\Controller; use SilverStripe\Dev\SapphireTest; use SilverStripe\GraphQL\Schema\Storage\AbstractTypeRegistry; use SilverStripe\GraphQL\Controller as GraphQLController; use Symfony\Component\Filesystem\Filesystem; use ReflectionObject; -use stdClass; use SilverStripe\Control\Session; use PHPUnit\Framework\Attributes\DataProvider; +use SilverStripe\GraphQL\Schema\Schema; class AbstractTypeRegistryTest extends SapphireTest { @@ -37,8 +37,9 @@ protected function tearDown(): void $fs->remove($dir); } // ensure that any GraphqlController added to controller_stack is removed - if (Controller::has_curr() && (Controller::curr() instanceof GraphQLController)) { - Controller::curr()->popCurrent(); + $controller = Controller::curr(); + if ($controller instanceof GraphQLController) { + $controller->popCurrent(); } } @@ -60,20 +61,7 @@ public function testRebuildOnMissing( bool $expected ): void { list($registry, $canRebuildOnMissingMethod, $_, $getRebuildOnMissingFilename) = $this->getInstance(); - $graphqlController = new GraphQLController('test'); - - // autobuild - $graphqlController->setAutobuildSchema($autobuild); - - // controller - if ($controller) { - // Make it so that Controller::curr() returns a GraphQLController - $fakeSession = new Session([]); - $request = Controller::curr()->getRequest(); - $request->setSession($fakeSession); - $graphqlController->setRequest($request); - $graphqlController->pushCurrent(); - } + $this->prepGraphQLController($controller, $autobuild); // config AbstractTypeRegistry::config()->set('rebuild_on_missing_schema_file', $config); @@ -137,6 +125,68 @@ public static function provideRebuildOnMissing(): array ]; } + /** + * This test checks no uncaught exceptions are thrown with a successful rebuild. + * This test is required separately from the others, because the other tests explicitly + * set some private methods to be accesible via reflection. + */ + public function testGetRebuildOnMissing(): void + { + $registry = new class extends AbstractTypeRegistry + { + private static int $getAttempts = 0; + + protected static function getSourceDirectory(): string + { + return AbstractTypeRegistryTest::SOURCE_DIRECTORY; + } + + protected static function getSourceNamespace(): string + { + return ''; + } + + protected static function fromCache(string $typename): bool + { + if (static::$getAttempts === 0) { + static::$getAttempts++; + throw new Exception('Missing graphql file for ' . $typename); + } + return true; + } + }; + $this->prepGraphQLController(true, true); + AbstractTypeRegistry::config()->set('rebuild_on_missing_schema_file', true); + Schema::config()->merge('schemas', [ + '.graphql-generated' => [], + ]); + + $registry::get('test'); + // This test passes by not throwing any exceptions. + $this->expectNotToPerformAssertions(); + } + + /** + * Prepare a GraphQLController for AbstractTypeRegistry::canRebuildOnMissing() checks. + */ + private function prepGraphQLController(bool $controller, bool $autobuild): void + { + $graphqlController = new GraphQLController('test'); + + // autobuild + $graphqlController->setAutobuildSchema($autobuild); + + // controller + if ($controller) { + // Make it so that Controller::curr() returns a GraphQLController + $fakeSession = new Session([]); + $request = Controller::curr()->getRequest(); + $request->setSession($fakeSession); + $graphqlController->setRequest($request); + $graphqlController->pushCurrent(); + } + } + private function getInstance() { $registry = new class extends AbstractTypeRegistry diff --git a/tests/Schema/_AbstractTypeRegistryTest/schema.yml b/tests/Schema/_AbstractTypeRegistryTest/schema.yml new file mode 100644 index 000000000..be354975b --- /dev/null +++ b/tests/Schema/_AbstractTypeRegistryTest/schema.yml @@ -0,0 +1,12 @@ +types: + MyType: + fields: + field1: String + field2: Int +queries: + readMyTypes: + type: '[MyType]' + resolver: [SilverStripe\GraphQL\Tests\Fake\IntegrationTestResolver, lotsOfMyTypes] + plugins: + paginate: + resolver: [SilverStripe\GraphQL\Tests\Fake\IntegrationTestResolver, testPaginate]