From a2b2788b1c849f033da9a47c75c361bcf5439ea9 Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Sat, 4 May 2024 02:07:58 +0200 Subject: [PATCH 1/7] tests: adding unit testing to App class --- tests/src/TestCase.php | 75 ++++++++++++++++++++++++ tests/unit/AppTest.php | 130 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 tests/unit/AppTest.php diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php index 9e313a2..e094119 100644 --- a/tests/src/TestCase.php +++ b/tests/src/TestCase.php @@ -5,8 +5,15 @@ namespace Inpsyde\App\Tests; use Brain\Monkey; +use Inpsyde\Modularity\Module\ExecutableModule; +use Inpsyde\Modularity\Module\ExtendingModule; +use Inpsyde\Modularity\Module\FactoryModule; +use Inpsyde\Modularity\Module\Module; +use Inpsyde\Modularity\Module\ServiceModule; +use Inpsyde\Modularity\Properties\Properties; use Inpsyde\WpContext; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; +use Mockery\MockInterface; use PHPUnit\Framework\TestCase as FrameworkTestCase; abstract class TestCase extends FrameworkTestCase @@ -58,6 +65,9 @@ protected function setUp(): void 'get_stylesheet_directory_uri' => static function (): string { return get_theme_root_uri() . '/my-theme'; }, + 'plugins_url' => static function (): string { + return content_url() . '/plugins'; + }, ]); } @@ -83,4 +93,69 @@ protected function factoryContext(?string $case = null, bool $withCli = false): return $withCli ? $context->withCli() : $context; } + + /** + * @param string $basename + * @param bool $isDebug + * + * @return Properties|MockInterface + */ + protected function mockProperties( + string $basename = 'basename', + bool $isDebug = false + ): Properties { + + $stub = \Mockery::mock(Properties::class); + $stub->allows('basename')->andReturn($basename); + $stub->allows('isDebug')->andReturn($isDebug); + + return $stub; + } + + /** + * @param string $id + * @param class-string ...$interfaces + * @return Module|MockInterface + */ + protected function mockModule(string $id = 'module', string ...$interfaces): Module + { + $interfaces or $interfaces[] = Module::class; + + $stub = \Mockery::mock(...$interfaces); + $stub->allows('id')->andReturn($id); + + if (in_array(ServiceModule::class, $interfaces, true) ) { + $stub->allows('services')->byDefault()->andReturn([]); + } + + if (in_array(FactoryModule::class, $interfaces, true) ) { + $stub->allows('factories')->byDefault()->andReturn([]); + } + + if (in_array(ExtendingModule::class, $interfaces, true) ) { + $stub->allows('extensions')->byDefault()->andReturn([]); + } + + if (in_array(ExecutableModule::class, $interfaces, true) ) { + $stub->allows('run')->byDefault()->andReturn(false); + } + + return $stub; + } + + /** + * @param string ...$ids + * @return array + */ + protected function stubServices(string ...$ids): array + { + $services = []; + foreach ($ids as $id) { + $services[$id] = static function () use ($id) { + return new \ArrayObject(['id' => $id]); + }; + } + + return $services; + } } diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php new file mode 100644 index 0000000..84b6671 --- /dev/null +++ b/tests/unit/AppTest.php @@ -0,0 +1,130 @@ +containerProp = $reflectedApp->getProperty('container'); + $this->containerProp->setAccessible(true); + $this->appStatusProp = $reflectedApp->getProperty('status'); + $this->appStatusProp->setAccessible(true); + $reflectedContainer = new \ReflectionClass(CompositeContainer::class); + $this->mapProp = $reflectedContainer->getProperty('map'); + $this->mapProp->setAccessible(true); + parent::setUp(); + } + + public function testNewWithNoContainer() + { + + $context = WpContext::new()->force(WpContext::CORE); + $app = App::new(null, null, $context); + static::assertInstanceOf( + CompositeContainer::class, + $this->containerProp->getValue($app) + ); + static::assertTrue($this->appStatusProp->getValue($app)->isIdle()); + } + + public function testSharePackageToBootShouldAddPackageServicesToContainerIfPackageIsBooted() + { + + \Brain\Monkey\Functions\stubs([ + 'plugins_url' => static function (): string { + return content_url() . '/plugins/fake'; + }, + ]); + + $context = WpContext::new()->force(WpContext::CORE); + $app = App::new(null, null, $context); + static::assertInstanceOf( + CompositeContainer::class, + $this->containerProp->getValue($app) + ); + $appModuleId = 'app-module-id'; + $containerServiceId = 'cont-service-id'; + + $appModule = $this->mockModule($appModuleId, ServiceModule::class); + $appModule->expects('services')->andReturn($this->stubServices($containerServiceId)); + $app->addModule($appModule); + + $moduleId = 'my-service-module'; + $packageServiceId = 'service-id'; + + $module = $this->mockModule($moduleId, ServiceModule::class); + $module->expects('services')->andReturn($this->stubServices($packageServiceId)); + + $package = Package::new($this->mockProperties())->addModule($module); + + static::assertTrue($package->boot()); + static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); + $container = $this->containerProp->getValue($app); + + static::assertTrue($container->has($packageServiceId)); + } + + public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBootIfPackageIsNotBooted() + { + + \Brain\Monkey\Functions\stubs([ + 'plugins_url' => static function (): string { + return content_url() . '/plugins/fake'; + }, + ]); + + $context = WpContext::new()->force(WpContext::CORE); + $app = App::new(null, null, $context); + static::assertInstanceOf( + CompositeContainer::class, + $this->containerProp->getValue($app) + ); + $appModuleId = 'app-module-id'; + $containerServiceId = 'cont-service-id'; + + $appModule = $this->mockModule($appModuleId, ServiceModule::class); + $appModule->expects('services')->andReturn($this->stubServices($containerServiceId)); + $app->addModule($appModule); + + $moduleId = 'my-service-module'; + $packageServiceId = 'service-id'; + + $module = $this->mockModule($moduleId, ServiceModule::class); + $module->expects('services')->andReturn($this->stubServices($packageServiceId)); + + $package = Package::new($this->mockProperties())->addModule($module); + + static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); + $container = $this->containerProp->getValue($app); + Monkey\Functions\when('remove_all_actions')->justReturn(); + + // we boot the app container + $app->boot(); + + static::assertTrue($container->has($packageServiceId)); + static::assertEquals([ "inpsyde-wp-app" => true ], $package->connectedPackages()); + + /** + * The following assert is failing + */ +// static::assertTrue($package->container()->has($containerServiceId)); + } + +} \ No newline at end of file From 9dd5ead2c1ee6f7a387a8a5e7c7084219b866bd5 Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Sat, 4 May 2024 14:17:15 +0200 Subject: [PATCH 2/7] chore: enhancing tests --- composer.json | 1 + src/App.php | 3 + tests/src/TestCase.php | 10 +-- tests/unit/AppTest.php | 137 ++++++++++++++++++++++++++--------------- 4 files changed, 98 insertions(+), 53 deletions(-) diff --git a/composer.json b/composer.json index 88e4e42..04c2248 100644 --- a/composer.json +++ b/composer.json @@ -49,6 +49,7 @@ "minimum-stability": "stable", "scripts": { "cs": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs", + "fix:cs": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf", "psalm": "@php ./vendor/vimeo/psalm/psalm --no-cache --output-format=compact", "tests": "@php ./vendor/phpunit/phpunit/phpunit", "tests:no-cov": "@php ./vendor/phpunit/phpunit/phpunit --no-coverage", diff --git a/src/App.php b/src/App.php index 2d2a7c5..4e85058 100644 --- a/src/App.php +++ b/src/App.php @@ -353,6 +353,9 @@ public function sharePackage(Modularity\Package $package, string ...$contexts): } /** + * If context is correct + * If package is booted + * Adds the Package Container to the App Container * @param Modularity\Package $package * @param string ...$contexts * @return App diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php index e094119..8e3493e 100644 --- a/tests/src/TestCase.php +++ b/tests/src/TestCase.php @@ -124,19 +124,19 @@ protected function mockModule(string $id = 'module', string ...$interfaces): Mod $stub = \Mockery::mock(...$interfaces); $stub->allows('id')->andReturn($id); - if (in_array(ServiceModule::class, $interfaces, true) ) { + if (in_array(ServiceModule::class, $interfaces, true)) { $stub->allows('services')->byDefault()->andReturn([]); } - if (in_array(FactoryModule::class, $interfaces, true) ) { + if (in_array(FactoryModule::class, $interfaces, true)) { $stub->allows('factories')->byDefault()->andReturn([]); } - if (in_array(ExtendingModule::class, $interfaces, true) ) { + if (in_array(ExtendingModule::class, $interfaces, true)) { $stub->allows('extensions')->byDefault()->andReturn([]); } - if (in_array(ExecutableModule::class, $interfaces, true) ) { + if (in_array(ExecutableModule::class, $interfaces, true)) { $stub->allows('run')->byDefault()->andReturn(false); } @@ -151,7 +151,7 @@ protected function stubServices(string ...$ids): array { $services = []; foreach ($ids as $id) { - $services[$id] = static function () use ($id) { + $services[$id] = static function () use ($id): \ArrayObject { return new \ArrayObject(['id' => $id]); }; } diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index 84b6671..8dcdb14 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -6,6 +6,7 @@ use Inpsyde\App\App; use Inpsyde\App\CompositeContainer; +use Inpsyde\Modularity\Container\ReadOnlyContainer; use Inpsyde\Modularity\Module\ServiceModule; use Inpsyde\Modularity\Package; use Inpsyde\WpContext; @@ -15,9 +16,14 @@ class AppTest extends TestCase { - private \ReflectionProperty $containerProp; - private \ReflectionProperty $mapProp; - private \ReflectionProperty $appStatusProp; + /** @var \ReflectionProperty */ + private $containerProp; + /** @var \ReflectionProperty */ + private $mapProp; + /** @var \ReflectionProperty */ + private $appStatusProp; + /** @var \ReflectionProperty */ + private $appBootQueueProp; protected function setUp(): void { @@ -29,24 +35,13 @@ protected function setUp(): void $reflectedContainer = new \ReflectionClass(CompositeContainer::class); $this->mapProp = $reflectedContainer->getProperty('map'); $this->mapProp->setAccessible(true); + $this->appBootQueueProp = $reflectedApp->getProperty('bootQueue'); + $this->appBootQueueProp->setAccessible(true); parent::setUp(); } - public function testNewWithNoContainer() - { - - $context = WpContext::new()->force(WpContext::CORE); - $app = App::new(null, null, $context); - static::assertInstanceOf( - CompositeContainer::class, - $this->containerProp->getValue($app) - ); - static::assertTrue($this->appStatusProp->getValue($app)->isIdle()); - } - - public function testSharePackageToBootShouldAddPackageServicesToContainerIfPackageIsBooted() + private function prepareShareToPackageCommon(): array { - \Brain\Monkey\Functions\stubs([ 'plugins_url' => static function (): string { return content_url() . '/plugins/fake'; @@ -74,57 +69,103 @@ public function testSharePackageToBootShouldAddPackageServicesToContainerIfPacka $package = Package::new($this->mockProperties())->addModule($module); - static::assertTrue($package->boot()); - static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); - $container = $this->containerProp->getValue($app); - - static::assertTrue($container->has($packageServiceId)); + return [ + 'app' => $app, + 'appModuleId' => $appModuleId, + 'containerServiceId' => $containerServiceId, + 'moduleId' => $moduleId, + 'packageServiceId' => $packageServiceId, + 'package' => $package, + ]; } - public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBootIfPackageIsNotBooted() + public function testNewWithNoContainer() { - \Brain\Monkey\Functions\stubs([ - 'plugins_url' => static function (): string { - return content_url() . '/plugins/fake'; - }, - ]); - $context = WpContext::new()->force(WpContext::CORE); $app = App::new(null, null, $context); static::assertInstanceOf( CompositeContainer::class, $this->containerProp->getValue($app) ); - $appModuleId = 'app-module-id'; - $containerServiceId = 'cont-service-id'; + static::assertTrue($this->appStatusProp->getValue($app)->isIdle()); + } - $appModule = $this->mockModule($appModuleId, ServiceModule::class); - $appModule->expects('services')->andReturn($this->stubServices($containerServiceId)); - $app->addModule($appModule); + public function testSharePackageToBootShouldAddPackageServicesToContainerIfPackageIsBooted() + { + [ + 'app' => $app, + 'containerServiceId' => $containerServiceId, + 'packageServiceId' => $packageServiceId, + 'package' => $package + ] = $this->prepareShareToPackageCommon(); + + // When package is booted + static::assertTrue($package->boot()); - $moduleId = 'my-service-module'; - $packageServiceId = 'service-id'; + // When we call sharePackageToBoot and pass a booted package + static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); - $module = $this->mockModule($moduleId, ServiceModule::class); - $module->expects('services')->andReturn($this->stubServices($packageServiceId)); + $container = $this->containerProp->getValue($app); - $package = Package::new($this->mockProperties())->addModule($module); + // The Services from the Package are shared to the App Container + static::assertTrue($container->has($packageServiceId)); + + // But, the services from the App Container are NOT added to the Package since the container is ReadOnly + static::assertInstanceOf(ReadOnlyContainer::class, $package->container()); + static::assertFalse($package->container()->has($containerServiceId)); + } + + public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBootIfPackageIsNotBooted() + { + [ + 'app' => $app, + 'containerServiceId' => $containerServiceId, + 'packageServiceId' => $packageServiceId, + 'package' => $package + ] = $this->prepareShareToPackageCommon(); + + Monkey\Functions\when('remove_all_actions')->justReturn(); + + // When we call sharePackageToBoot passing a NOT booted package static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); + + // we don't expect the services from the Package to be in the App Container before booting the App Container $container = $this->containerProp->getValue($app); - Monkey\Functions\when('remove_all_actions')->justReturn(); + static::assertFalse($container->has($packageServiceId)); + + // we don't expect the package to have container to be accesible because is not booted nor built. + static::assertFalse($package->statusIs(Package::STATUS_BOOTED)); + try { + $package->container(); + } catch (\Exception $exception) { + static::assertStringContainsString( + 'Can\'t obtain the container', + $exception->getMessage() + ); + } + + // It is only connected with a new Package that is booted already + static::assertEquals([ "inpsyde-wp-app" => true ], $package->connectedPackages()); - // we boot the app container + // We expect the Package to be added to the boot queue + /** @var \SplObjectStorage $currentQueue */ + $currentQueue = $this->appBootQueueProp->getValue($app); + static::assertTrue($currentQueue->contains($package)); + + // if we boot the App container $app->boot(); + $container = $this->containerProp->getValue($app); + // We expect the App Container to have the Package services static::assertTrue($container->has($packageServiceId)); - static::assertEquals([ "inpsyde-wp-app" => true ], $package->connectedPackages()); - /** - * The following assert is failing - */ -// static::assertTrue($package->container()->has($containerServiceId)); - } + // We expect the Package to be booted + static::assertTrue($package->statusIs(Package::STATUS_BOOTED)); -} \ No newline at end of file + // we expect the App Container services to be in the Package + // sharePackageToBoot is meant to boot the Package + static::assertFalse($package->container()->has($containerServiceId)); + } +} From f3edf290e554f7c9ad2d109f337905adc9da0048 Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Sat, 4 May 2024 14:20:01 +0200 Subject: [PATCH 3/7] chore: comment failing test --- tests/unit/AppTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index 8dcdb14..4911695 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -166,6 +166,7 @@ public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBo // we expect the App Container services to be in the Package // sharePackageToBoot is meant to boot the Package - static::assertFalse($package->container()->has($containerServiceId)); + // TODO: FIX THIS +// static::assertTrue($package->container()->has($containerServiceId)); } } From 6292b7f915ab3f21244815fe8c9a5f307d972a5c Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Mon, 6 May 2024 12:06:01 +0200 Subject: [PATCH 4/7] tests: adding unit testing addModule and AddEarlyModule --- src/App.php | 2 +- tests/unit/AppTest.php | 214 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 192 insertions(+), 24 deletions(-) diff --git a/src/App.php b/src/App.php index 4e85058..4910847 100644 --- a/src/App.php +++ b/src/App.php @@ -478,7 +478,6 @@ private function addModularityModule( return $this; } - $this->initializeModularity($early); $this->ensureWillBoot(); @@ -637,6 +636,7 @@ private function handleModularityBoot( : Modularity\Package::MODULE_EXECUTED; $this->syncModularityStatus($package, $status); + // TODO: this if condition is running twice when called from sharePackage or sharePackageToBoot if ( $package->statusIs(Modularity\Package::STATUS_BOOTED) || ($onPackageReady && $package->statusIs(Modularity\Package::STATUS_INITIALIZED)) diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index 4911695..eaacdfb 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -10,8 +10,6 @@ use Inpsyde\Modularity\Module\ServiceModule; use Inpsyde\Modularity\Package; use Inpsyde\WpContext; -use Psr\Container\ContainerInterface; -use Inpsyde\App\Config\Config; use Brain\Monkey; class AppTest extends TestCase @@ -24,9 +22,14 @@ class AppTest extends TestCase private $appStatusProp; /** @var \ReflectionProperty */ private $appBootQueueProp; + /** @var \ReflectionMethod */ + private $appHandleModularityBoot; + /** @var \ReflectionMethod */ + private $appSyncModularityStatus; protected function setUp(): void { + Monkey\Functions\when('remove_all_actions')->justReturn(); $reflectedApp = new \ReflectionClass(App::class); $this->containerProp = $reflectedApp->getProperty('container'); $this->containerProp->setAccessible(true); @@ -37,10 +40,12 @@ protected function setUp(): void $this->mapProp->setAccessible(true); $this->appBootQueueProp = $reflectedApp->getProperty('bootQueue'); $this->appBootQueueProp->setAccessible(true); + $this->appHandleModularityBoot = $reflectedApp->getMethod('handleModularityBoot'); + $this->appSyncModularityStatus = $reflectedApp->getMethod('syncModularityStatus'); parent::setUp(); } - private function prepareShareToPackageCommon(): array + private function prepareSharePackageCommon(): array { \Brain\Monkey\Functions\stubs([ 'plugins_url' => static function (): string { @@ -59,13 +64,14 @@ private function prepareShareToPackageCommon(): array $appModule = $this->mockModule($appModuleId, ServiceModule::class); $appModule->expects('services')->andReturn($this->stubServices($containerServiceId)); - $app->addModule($appModule); + $app->addEarlyModule($appModule); $moduleId = 'my-service-module'; $packageServiceId = 'service-id'; $module = $this->mockModule($moduleId, ServiceModule::class); - $module->expects('services')->andReturn($this->stubServices($packageServiceId)); + $expectedReturnFromService = $this->stubServices($packageServiceId); + $module->expects('services')->andReturn($expectedReturnFromService); $package = Package::new($this->mockProperties())->addModule($module); @@ -76,6 +82,7 @@ private function prepareShareToPackageCommon(): array 'moduleId' => $moduleId, 'packageServiceId' => $packageServiceId, 'package' => $package, + 'expectedClassFromPackageService' => \ArrayObject::class, ]; } @@ -91,14 +98,56 @@ public function testNewWithNoContainer() static::assertTrue($this->appStatusProp->getValue($app)->isIdle()); } - public function testSharePackageToBootShouldAddPackageServicesToContainerIfPackageIsBooted() + public function testAddEarlyModule() + { + $context = WpContext::new()->force(WpContext::CORE); + $app = App::new(null, null, $context); + $moduleId = 'my-early-service-module'; + $moduleServiceId = 'early-service-id'; + $module = $this->mockModule($moduleId, ServiceModule::class); + $module->expects('services')->andReturn($this->stubServices($moduleServiceId)); + $app->addEarlyModule($module); + // We expect the service is not resolvable if the App Container is not booted + static::assertEquals(null, $app->resolve($moduleServiceId)); + $app->boot(); + static::assertInstanceOf(\ArrayObject::class, $app->resolve($moduleServiceId)); + } + + public function testAddModule() + { + $context = WpContext::new()->force(WpContext::CORE); + $app = App::new(null, null, $context); + $moduleId = 'my-early-service-module'; + $moduleServiceId = 'early-service-id'; + $module = $this->mockModule($moduleId, ServiceModule::class); + $module->expects('services')->andReturn($this->stubServices($moduleServiceId)); + $app->addModule($module); + // We expect the service is not resolvable if the App Container is not booted + static::assertEquals(null, $app->resolve($moduleServiceId)); + $app->boot(); + // TODO: check how addModule was meant to work + static::assertInstanceOf(\ArrayObject::class, $app->resolve($moduleServiceId)); + } + + /** + * Scenario + * Package is booted + * Expectations + * Package services are added to the App Container + * Package does NOT receive any definitions from the WP App Container + * + * @group sharePackageToBoot + * @return void + */ + public function testSharePackageToBootWhenPackageIsBooted() { [ 'app' => $app, 'containerServiceId' => $containerServiceId, 'packageServiceId' => $packageServiceId, - 'package' => $package - ] = $this->prepareShareToPackageCommon(); + 'package' => $package, + 'expectedClassFromPackageService' => $expectedClassFromPackageService, + ] = $this->prepareSharePackageCommon(); // When package is booted static::assertTrue($package->boot()); @@ -106,29 +155,38 @@ public function testSharePackageToBootShouldAddPackageServicesToContainerIfPacka // When we call sharePackageToBoot and pass a booted package static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); - $container = $this->containerProp->getValue($app); - // The Services from the Package are shared to the App Container - static::assertTrue($container->has($packageServiceId)); + static::assertInstanceOf($expectedClassFromPackageService, $app->resolve($packageServiceId)); - // But, the services from the App Container are NOT added to the Package since the container is ReadOnly - static::assertInstanceOf(ReadOnlyContainer::class, $package->container()); + // But, the services from the App Container are NOT added to the Package since the Package Container is ReadOnly static::assertFalse($package->container()->has($containerServiceId)); } - public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBootIfPackageIsNotBooted() + /** + * Scenario + * Package is not booted + * The Services added to the App Container are added via addEarlyModule + * App boot is called after + * Expectations + * After App Booting + * App Container can resolve Package services + * Package can resolve App Container Services + * @group sharePackageToBoot + * @return void + */ + public function testSharePackageToBootWhenPackageIsNotBooted(): void { [ 'app' => $app, 'containerServiceId' => $containerServiceId, 'packageServiceId' => $packageServiceId, - 'package' => $package - ] = $this->prepareShareToPackageCommon(); + 'package' => $package, + ] = $this->prepareSharePackageCommon(); - Monkey\Functions\when('remove_all_actions')->justReturn(); // When we call sharePackageToBoot passing a NOT booted package + // Notice that the WP App container is not booted at this point neither static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); // we don't expect the services from the Package to be in the App Container before booting the App Container @@ -154,19 +212,129 @@ public function testSharePackageToBootShouldAddPackageServicesToContainerAfterBo $currentQueue = $this->appBootQueueProp->getValue($app); static::assertTrue($currentQueue->contains($package)); + // It seems the App Container does not have the service in the container if it is not booted + $container = $this->containerProp->getValue($app); + static::assertFalse($container->has($containerServiceId)); + // if we boot the App container $app->boot(); - $container = $this->containerProp->getValue($app); // We expect the App Container to have the Package services - static::assertTrue($container->has($packageServiceId)); + static::assertInstanceOf(\ArrayObject::class, $app->resolve($packageServiceId)); // We expect the Package to be booted static::assertTrue($package->statusIs(Package::STATUS_BOOTED)); - // we expect the App Container services to be in the Package - // sharePackageToBoot is meant to boot the Package - // TODO: FIX THIS -// static::assertTrue($package->container()->has($containerServiceId)); + // Note: we can retrieve the service from the App Container because we used App::addEarlyModule + static::assertTrue($package->container()->has($containerServiceId)); + static::assertInstanceOf(\ArrayObject::class, $app->resolve($containerServiceId)); + } + + + public function testSharePackageWhenPackageIsBooted() + { + /** + * @var App $app + */ + [ + 'app' => $app, + 'containerServiceId' => $containerServiceId, + 'packageServiceId' => $packageServiceId, + 'package' => $package, + 'expectedClassFromPackageService' => $expectedClassFromPackageService, + ] = $this->prepareSharePackageCommon(); + + // When package is booted + static::assertTrue($package->boot()); + + // When we call sharePackage and pass a booted package + static::assertInstanceOf(App::class, $app->sharePackage($package)); + + // The Services from the Package are shared to the App Container + static::assertInstanceOf($expectedClassFromPackageService, $app->resolve($packageServiceId)); + + // But, the services from the App Container are NOT added to the Package since the Package Container is ReadOnly + static::assertFalse($package->container()->has($containerServiceId)); + } + + + /** + * Scenario + * Package is not booted + * The Services added to the App Container are added via addEarlyModule + * App boot is called after + * Expectations + * After App Booting + * App Container can resolve Package services + * Package can resolve App Container Services + * @group sharePackageToBoot + * @return void + */ + public function testSharePackageWhenPackageIsNotBooted(): void + { + + [ + 'app' => $app, + 'containerServiceId' => $containerServiceId, + 'packageServiceId' => $packageServiceId, + 'package' => $package, + ] = $this->prepareSharePackageCommon(); + + + + // When we call sharePackageToBoot passing a NOT booted package + // Notice that the WP App container is not booted at this point neither + static::assertInstanceOf(App::class, $app->sharePackage($package)); + + // we don't expect the services from the Package to be in the App Container before booting the App Container + $container = $this->containerProp->getValue($app); + static::assertFalse($container->has($packageServiceId)); + + // we don't expect the package to have container to be accessible because is not booted nor built. + static::assertFalse($package->statusIs(Package::STATUS_BOOTED)); + try { + $package->container(); + } catch (\Exception $exception) { + static::assertStringContainsString( + 'Can\'t obtain the container', + $exception->getMessage() + ); + } + + // It is only connected with a new Package that is booted already + static::assertEquals([ "inpsyde-wp-app" => true ], $package->connectedPackages()); + + // we manually boot the package in here since sharePackage enqueues a callback waiting for this + $package->boot(); + + // we have to mimic the internals of the waitForPackageBoot + // (we are hardcoding a callback there) + $this->appSyncModularityStatus->invoke( + $app, + $package, + Package::MODULE_ADDED + ); + + $this->appHandleModularityBoot->invoke( + $app, + $package, + true, + ); + + // package can't see the service from app container if the Wp App Container is not booted + static::assertFalse($package->container()->has($containerServiceId)); + + // if we boot the App container + $app->boot(); + + // We expect the Package to have container services + static::assertTrue($package->container()->has($containerServiceId)); + + // We expect the App Container to have the Package services + static::assertInstanceOf(\ArrayObject::class, $app->resolve($packageServiceId)); + + // Note: we can retrieve the service from the App Container because we used App::addEarlyModule + static::assertInstanceOf(\ArrayObject::class, $app->resolve($containerServiceId)); } + } From 6fc4bb265da53682036ca0fe639e34504f198fa6 Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Mon, 6 May 2024 12:07:39 +0200 Subject: [PATCH 5/7] chore: fix cs --- tests/unit/AppTest.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index eaacdfb..cfb412f 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -6,7 +6,6 @@ use Inpsyde\App\App; use Inpsyde\App\CompositeContainer; -use Inpsyde\Modularity\Container\ReadOnlyContainer; use Inpsyde\Modularity\Module\ServiceModule; use Inpsyde\Modularity\Package; use Inpsyde\WpContext; @@ -184,7 +183,6 @@ public function testSharePackageToBootWhenPackageIsNotBooted(): void 'package' => $package, ] = $this->prepareSharePackageCommon(); - // When we call sharePackageToBoot passing a NOT booted package // Notice that the WP App container is not booted at this point neither static::assertInstanceOf(App::class, $app->sharePackageToBoot($package)); @@ -230,7 +228,6 @@ public function testSharePackageToBootWhenPackageIsNotBooted(): void static::assertInstanceOf(\ArrayObject::class, $app->resolve($containerServiceId)); } - public function testSharePackageWhenPackageIsBooted() { /** @@ -257,7 +254,6 @@ public function testSharePackageWhenPackageIsBooted() static::assertFalse($package->container()->has($containerServiceId)); } - /** * Scenario * Package is not booted @@ -280,8 +276,6 @@ public function testSharePackageWhenPackageIsNotBooted(): void 'package' => $package, ] = $this->prepareSharePackageCommon(); - - // When we call sharePackageToBoot passing a NOT booted package // Notice that the WP App container is not booted at this point neither static::assertInstanceOf(App::class, $app->sharePackage($package)); @@ -318,7 +312,7 @@ public function testSharePackageWhenPackageIsNotBooted(): void $this->appHandleModularityBoot->invoke( $app, $package, - true, + true ); // package can't see the service from app container if the Wp App Container is not booted @@ -336,5 +330,4 @@ public function testSharePackageWhenPackageIsNotBooted(): void // Note: we can retrieve the service from the App Container because we used App::addEarlyModule static::assertInstanceOf(\ArrayObject::class, $app->resolve($containerServiceId)); } - } From 74f85f1de5d3f84b80189714e22c5734bf2bd9d9 Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Mon, 6 May 2024 13:27:44 +0200 Subject: [PATCH 6/7] tests: align addModule tests --- tests/example/cases/ProjectTest.php | 16 ++++++++++++++ .../modularity-lib/src/LateModule.php | 19 +++++++++++++++++ tests/unit/AppTest.php | 21 ++++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/example/cases/ProjectTest.php b/tests/example/cases/ProjectTest.php index 3a056ee..da6831e 100644 --- a/tests/example/cases/ProjectTest.php +++ b/tests/example/cases/ProjectTest.php @@ -26,11 +26,23 @@ public function executeProject(): void fwrite(STDOUT, print_r(app()->resolve(Logger::class)->allLogs(), true)); } + protected function checkLateModule(bool $expectToBeThere = false): void + { + if (!$expectToBeThere) { + static::assertTrue(app()->status()->isBooted()); + static::assertNull(app()->resolve('dummy-test')); + return; + } + static::assertNotNull(app()->resolve('dummy-test')); + static::assertEquals(app()->resolve('dummy-test')->text(), 'Hello World'); + } + /** * @return void */ protected function onBeforePlugins(): void { + $this->checkLateModule(); } /** @@ -38,6 +50,7 @@ protected function onBeforePlugins(): void */ protected function onAfterPlugins(): void { + $this->checkLateModule(); } /** @@ -45,6 +58,7 @@ protected function onAfterPlugins(): void */ protected function onAfterTheme(): void { + $this->checkLateModule(); } /** @@ -61,6 +75,8 @@ protected function onAfterInit(): void static::assertTrue($logger->hasLog("{$pre} 'Lorem Ipsum'")); static::assertTrue($logger->hasLog("{$pre} 'Dolor Sit Amet'")); static::assertFalse($logger->hasLog("{$pre} '[From Plugin 2] Plugin Two is Good For You'")); + + $this->checkLateModule(true); } /** diff --git a/tests/example/sources/libraries/modularity-lib/src/LateModule.php b/tests/example/sources/libraries/modularity-lib/src/LateModule.php index e1c0c3d..f2539f9 100644 --- a/tests/example/sources/libraries/modularity-lib/src/LateModule.php +++ b/tests/example/sources/libraries/modularity-lib/src/LateModule.php @@ -5,6 +5,7 @@ namespace Inpsyde\App\Tests\Project\ModularityLib; use Inpsyde\App\Tests\Project\ModularityPlugin3\Calc; +use Inpsyde\Modularity\Module\ServiceModule; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; @@ -28,4 +29,22 @@ static function () use ($container): void { return parent::run($container); } + + public function services(): array + { + return array_merge( + parent::services(), + [ + // phpcs:ignore Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType + 'dummy-test' => static function () { + return new class { + public function text(): string + { + return 'Hello World'; + } + }; + }, + ] + ); + } } diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index cfb412f..1335d1f 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -5,6 +5,7 @@ namespace Inpsyde\App\Tests; use Inpsyde\App\App; +use Inpsyde\App\AppStatus; use Inpsyde\App\CompositeContainer; use Inpsyde\Modularity\Module\ServiceModule; use Inpsyde\Modularity\Package; @@ -25,6 +26,8 @@ class AppTest extends TestCase private $appHandleModularityBoot; /** @var \ReflectionMethod */ private $appSyncModularityStatus; + /** @var \ReflectionClass */ + private $appStatusReflection; protected function setUp(): void { @@ -41,6 +44,9 @@ protected function setUp(): void $this->appBootQueueProp->setAccessible(true); $this->appHandleModularityBoot = $reflectedApp->getMethod('handleModularityBoot'); $this->appSyncModularityStatus = $reflectedApp->getMethod('syncModularityStatus'); + + $this->appStatusReflection = new \ReflectionClass(AppStatus::class); + parent::setUp(); } @@ -112,6 +118,11 @@ public function testAddEarlyModule() static::assertInstanceOf(\ArrayObject::class, $app->resolve($moduleServiceId)); } + /** + * + * @return void + * @throws \ReflectionException + */ public function testAddModule() { $context = WpContext::new()->force(WpContext::CORE); @@ -123,8 +134,16 @@ public function testAddModule() $app->addModule($module); // We expect the service is not resolvable if the App Container is not booted static::assertEquals(null, $app->resolve($moduleServiceId)); + + // we have to force the internal status of the AppStatus + // we need $lastRun to be true when calling isThemeStep inside boot + $appStatusInternalStatusProp = $this->appStatusReflection->getProperty('status'); + $appStatusInternalStatusProp->setValue( + $this->appStatusProp->getValue($app), + AppStatus::REGISTERING_THEMES + ); + $app->boot(); - // TODO: check how addModule was meant to work static::assertInstanceOf(\ArrayObject::class, $app->resolve($moduleServiceId)); } From c56b11a63d18183a3f224f02c71f47df37ef057c Mon Sep 17 00:00:00 2001 From: Luis Rosales Date: Mon, 6 May 2024 13:34:31 +0200 Subject: [PATCH 7/7] tests: align group names --- tests/unit/AppTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/unit/AppTest.php b/tests/unit/AppTest.php index 1335d1f..1a8dca8 100644 --- a/tests/unit/AppTest.php +++ b/tests/unit/AppTest.php @@ -247,6 +247,10 @@ public function testSharePackageToBootWhenPackageIsNotBooted(): void static::assertInstanceOf(\ArrayObject::class, $app->resolve($containerServiceId)); } + /** + * @return void + * @group sharePackage + */ public function testSharePackageWhenPackageIsBooted() { /** @@ -282,7 +286,7 @@ public function testSharePackageWhenPackageIsBooted() * After App Booting * App Container can resolve Package services * Package can resolve App Container Services - * @group sharePackageToBoot + * @group sharePackage * @return void */ public function testSharePackageWhenPackageIsNotBooted(): void