From 4dffdfff38ee8e9ab322e965d17a31ec4c6c0eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 17:08:16 +0200 Subject: [PATCH 1/6] chore(phpat): install codely fork --- composer.json | 16 +++++-- composer.lock | 119 +++++++++++++++++++++++++++++++++++++++++++++++--- phpstan.neon | 5 +++ 3 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 phpstan.neon diff --git a/composer.json b/composer.json index 16cbae255..1127dafd0 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,10 @@ "rector/rector": "^0.18.4", "psalm/plugin-mockery": "^1.1", "psalm/plugin-symfony": "^5.0", - "psalm/plugin-phpunit": "^0.18.4" + "psalm/plugin-phpunit": "^0.18.4", + "phpstan/phpstan": "^1.10", + "phpstan/extension-installer": "^1.3", + "phpat/phpat": "dev-add-has_one_public_method" }, "autoload": { "psr-4": { @@ -78,7 +81,14 @@ "minimum-stability": "RC", "config": { "allow-plugins": { - "ocramius/package-versions": true + "ocramius/package-versions": true, + "phpstan/extension-installer": true } - } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/CodelyTV/phpat" + } + ] } diff --git a/composer.lock b/composer.lock index 4c584ce32..8d2f3dfcd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aee8db87d99805f8edc889d34f98ff95", + "content-hash": "cebad5559ff66948d60a6d7997367d26", "packages": [ { "name": "brick/math", @@ -7234,6 +7234,66 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpat/phpat", + "version": "dev-add-has_one_public_method", + "source": { + "type": "git", + "url": "https://github.com/CodelyTV/phpat.git", + "reference": "5d530db9735aa52ca702db9e6d91493b1e06c990" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CodelyTV/phpat/zipball/5d530db9735aa52ca702db9e6d91493b1e06c990", + "reference": "5d530db9735aa52ca702db9e6d91493b1e06c990", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^1.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "kubawerlos/php-cs-fixer-custom-fixers": "^3.16", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "^4.0 || ^5.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPat\\": "src/" + }, + "files": [ + "helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\PHPat\\": "tests/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carlos Alandete Sastre", + "email": "carlos.alandete@gmail.com" + } + ], + "description": "PHP Architecture Tester", + "support": { + "source": "https://github.com/CodelyTV/phpat/tree/add-has_one_public_method" + }, + "time": "2023-10-03T14:53:59+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -7402,6 +7462,50 @@ }, "time": "2023-08-12T11:01:26+00:00" }, + { + "name": "phpstan/extension-installer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" + }, + "time": "2023-05-24T08:59:17+00:00" + }, { "name": "phpstan/phpdoc-parser", "version": "1.24.2", @@ -7451,16 +7555,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.35", + "version": "1.10.37", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3" + "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e730e5facb75ffe09dfb229795e8c01a459f26c3", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/058ba07e92f744d4dcf6061ae75283d0c6456f2e", + "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e", "shasum": "" }, "require": { @@ -7509,7 +7613,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T15:27:56+00:00" + "time": "2023-10-02T16:18:37+00:00" }, { "name": "phpunit/php-code-coverage", @@ -10516,7 +10620,8 @@ "aliases": [], "minimum-stability": "RC", "stability-flags": { - "roave/security-advisories": 20 + "roave/security-advisories": 20, + "phpat/phpat": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 000000000..51707b0d2 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +services: + - + class: Tests\Architecture\MyFirstTest + tags: + - phpat.test From 946fb031c5211ed706a79d0e2872c2168d73af18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 17:38:40 +0200 Subject: [PATCH 2/6] chore(phpat): test shared domain architecture --- composer.json | 4 +-- composer.lock | 46 +-------------------------- phpstan.neon | 22 ++++++++++--- tests/Shared/ArchitectureTest.php | 52 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 52 deletions(-) create mode 100644 tests/Shared/ArchitectureTest.php diff --git a/composer.json b/composer.json index 1127dafd0..0028fa52f 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,6 @@ "psalm/plugin-symfony": "^5.0", "psalm/plugin-phpunit": "^0.18.4", "phpstan/phpstan": "^1.10", - "phpstan/extension-installer": "^1.3", "phpat/phpat": "dev-add-has_one_public_method" }, "autoload": { @@ -81,8 +80,7 @@ "minimum-stability": "RC", "config": { "allow-plugins": { - "ocramius/package-versions": true, - "phpstan/extension-installer": true + "ocramius/package-versions": true } }, "repositories": [ diff --git a/composer.lock b/composer.lock index 8d2f3dfcd..3369971a0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cebad5559ff66948d60a6d7997367d26", + "content-hash": "cc21804655685b24332ea14a43ca7875", "packages": [ { "name": "brick/math", @@ -7462,50 +7462,6 @@ }, "time": "2023-08-12T11:01:26+00:00" }, - { - "name": "phpstan/extension-installer", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/phpstan/extension-installer.git", - "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", - "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0", - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.9.0" - }, - "require-dev": { - "composer/composer": "^2.0", - "php-parallel-lint/php-parallel-lint": "^1.2.0", - "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPStan\\ExtensionInstaller\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPStan\\ExtensionInstaller\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Composer plugin for automatic installation of PHPStan extensions", - "support": { - "issues": "https://github.com/phpstan/extension-installer/issues", - "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" - }, - "time": "2023-05-24T08:59:17+00:00" - }, { "name": "phpstan/phpdoc-parser", "version": "1.24.2", diff --git a/phpstan.neon b/phpstan.neon index 51707b0d2..9ae8853a1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,19 @@ +includes: + - vendor/phpat/phpat/extension.neon + +parameters: + level: 0 + paths: + - ./apps + - ./src + - ./tests + excludePaths: + - ./apps/backoffice/backend/var + - ./apps/backoffice/frontend/var + - ./apps/mooc/backend/var + - ./apps/mooc/frontend/var services: - - - class: Tests\Architecture\MyFirstTest - tags: - - phpat.test + - + class: CodelyTv\Tests\Shared\ArchitectureTest + tags: + - phpat.test diff --git a/tests/Shared/ArchitectureTest.php b/tests/Shared/ArchitectureTest.php new file mode 100644 index 000000000..e22701f20 --- /dev/null +++ b/tests/Shared/ArchitectureTest.php @@ -0,0 +1,52 @@ +classes(Selector::inNamespace('CodelyTv\Shared\Domain')) + ->canOnlyDependOn() + ->classes( + Selector::inNamespace('CodelyTv\Shared'), + // Dependencies + Selector::classname(Uuid::class), + // Language classes + Selector::classname(Throwable::class), + Selector::classname(InvalidArgumentException::class), + Selector::classname(RuntimeException::class), + Selector::classname(DateTimeImmutable::class), + Selector::classname(DateTimeInterface::class), + Selector::classname(DomainException::class), + Selector::classname(Stringable::class), + Selector::classname(BackedEnum::class), + Selector::classname(Countable::class), + Selector::classname(IteratorAggregate::class), + Selector::classname(Traversable::class), + Selector::classname(ArrayIterator::class), + Selector::classname(ReflectionClass::class), + ) + ->because('shared domain cannot import from outside'); + } +} From 0a8f1a8b086305f2f35c6318c7383e28233a0824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 17:40:40 +0200 Subject: [PATCH 3/6] ci: test architecture --- .github/workflows/ci.yml | 3 +++ Makefile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ab37e216..f629db424 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,9 @@ jobs: - name: 🏁 Static analysis run: make static-analysis + - name: 🏗️ Architecture + run: make test-architecture + - name: 🦭 Wait for the database to get up run: | while ! make ping-mysql &>/dev/null; do diff --git a/Makefile b/Makefile index aa5c306bb..3581505b7 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,9 @@ static-analysis: lint: docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/ecs check +test-architecture: + docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpstan analyse + start: @if [ ! -f .env.local ]; then echo '' > .env.local; fi UID=${shell id -u} GID=${shell id -g} docker compose up --build -d From 4ae23d96b2586abcf82e72c3aa5dd3b502224fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 17:55:10 +0200 Subject: [PATCH 4/6] chore(phpat): test shared infrastructure architecture --- phpstan.neon | 2 +- tests/Shared/ArchitectureTest.php | 52 ----------------- tests/Shared/SharedArchitectureTest.php | 76 +++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 53 deletions(-) delete mode 100644 tests/Shared/ArchitectureTest.php create mode 100644 tests/Shared/SharedArchitectureTest.php diff --git a/phpstan.neon b/phpstan.neon index 9ae8853a1..e0cc48dcd 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -14,6 +14,6 @@ parameters: - ./apps/mooc/frontend/var services: - - class: CodelyTv\Tests\Shared\ArchitectureTest + class: CodelyTv\Tests\Shared\SharedArchitectureTest tags: - phpat.test diff --git a/tests/Shared/ArchitectureTest.php b/tests/Shared/ArchitectureTest.php deleted file mode 100644 index e22701f20..000000000 --- a/tests/Shared/ArchitectureTest.php +++ /dev/null @@ -1,52 +0,0 @@ -classes(Selector::inNamespace('CodelyTv\Shared\Domain')) - ->canOnlyDependOn() - ->classes( - Selector::inNamespace('CodelyTv\Shared'), - // Dependencies - Selector::classname(Uuid::class), - // Language classes - Selector::classname(Throwable::class), - Selector::classname(InvalidArgumentException::class), - Selector::classname(RuntimeException::class), - Selector::classname(DateTimeImmutable::class), - Selector::classname(DateTimeInterface::class), - Selector::classname(DomainException::class), - Selector::classname(Stringable::class), - Selector::classname(BackedEnum::class), - Selector::classname(Countable::class), - Selector::classname(IteratorAggregate::class), - Selector::classname(Traversable::class), - Selector::classname(ArrayIterator::class), - Selector::classname(ReflectionClass::class), - ) - ->because('shared domain cannot import from outside'); - } -} diff --git a/tests/Shared/SharedArchitectureTest.php b/tests/Shared/SharedArchitectureTest.php new file mode 100644 index 000000000..afe480681 --- /dev/null +++ b/tests/Shared/SharedArchitectureTest.php @@ -0,0 +1,76 @@ +classes(Selector::inNamespace('CodelyTv\Shared\Domain')) + ->canOnlyDependOn() + ->classes(...array_merge($this->languageClasses(), [ + // Itself + Selector::inNamespace('CodelyTv\Shared\Domain'), + // Dependencies treated as domain + Selector::classname(Uuid::class), + ])) + ->because('shared domain cannot import from outside'); + } + + public function test_shared_infrastructure_should_not_import_from_other_contexts(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('CodelyTv\Shared\Infrastructure')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('CodelyTv')) + ->excluding( + // Itself + Selector::inNamespace('CodelyTv\Shared'), + // This need to be refactored + Selector::classname(MySqlDatabaseCleaner::class), + Selector::classname(AuthenticateUserCommand::class), + ); + } + + private function languageClasses(): array + { + return [ + Selector::classname(Throwable::class), + Selector::classname(InvalidArgumentException::class), + Selector::classname(RuntimeException::class), + Selector::classname(DateTimeImmutable::class), + Selector::classname(DateTimeInterface::class), + Selector::classname(DomainException::class), + Selector::classname(Stringable::class), + Selector::classname(BackedEnum::class), + Selector::classname(Countable::class), + Selector::classname(IteratorAggregate::class), + Selector::classname(Traversable::class), + Selector::classname(ArrayIterator::class), + Selector::classname(ReflectionClass::class), + ]; + } +} From ebdca743aaec116942ed258718d8c823cffb49f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 18:38:36 +0200 Subject: [PATCH 5/6] chore(phpat): test shared infrastructure architecture --- phpstan.neon | 6 ++ src/Shared/Domain/Utils.php | 9 --- .../Symfony/ApiExceptionListener.php | 10 +++- tests/Mooc/MoocArchitectureTest.php | 56 +++++++++++++++++++ .../Infrastructure/ArchitectureTest.php | 40 +++++++++++++ tests/Shared/SharedArchitectureTest.php | 35 +----------- 6 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 tests/Mooc/MoocArchitectureTest.php create mode 100644 tests/Shared/Infrastructure/ArchitectureTest.php diff --git a/phpstan.neon b/phpstan.neon index e0cc48dcd..4404d69b2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,8 +12,14 @@ parameters: - ./apps/backoffice/frontend/var - ./apps/mooc/backend/var - ./apps/mooc/frontend/var + services: - class: CodelyTv\Tests\Shared\SharedArchitectureTest tags: - phpat.test + + - + class: CodelyTv\Tests\Mooc\MoocArchitectureTest + tags: + - phpat.test diff --git a/src/Shared/Domain/Utils.php b/src/Shared/Domain/Utils.php index c7825a25b..df64bfb87 100644 --- a/src/Shared/Domain/Utils.php +++ b/src/Shared/Domain/Utils.php @@ -6,9 +6,7 @@ use DateTimeImmutable; use DateTimeInterface; -use ReflectionClass; use RuntimeException; - use function Lambdish\Phunctional\filter; final class Utils @@ -81,13 +79,6 @@ public static function filesIn(string $path, string $fileType): array ); } - public static function extractClassName(object $object): string - { - $reflect = new ReflectionClass($object); - - return $reflect->getShortName(); - } - public static function iterableToArray(iterable $iterable): array { if (is_array($iterable)) { diff --git a/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php b/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php index a18ea1e20..80ef312df 100644 --- a/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php +++ b/src/Shared/Infrastructure/Symfony/ApiExceptionListener.php @@ -6,6 +6,7 @@ use CodelyTv\Shared\Domain\DomainError; use CodelyTv\Shared\Domain\Utils; +use ReflectionClass; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Throwable; @@ -35,6 +36,13 @@ private function exceptionCodeFor(Throwable $error): string return $error instanceof $domainErrorClass ? $error->errorCode() - : Utils::toSnakeCase(Utils::extractClassName($error)); + : Utils::toSnakeCase($this->extractClassName($error)); + } + + private function extractClassName(object $object): string + { + $reflect = new ReflectionClass($object); + + return $reflect->getShortName(); } } diff --git a/tests/Mooc/MoocArchitectureTest.php b/tests/Mooc/MoocArchitectureTest.php new file mode 100644 index 000000000..a1cf75f57 --- /dev/null +++ b/tests/Mooc/MoocArchitectureTest.php @@ -0,0 +1,56 @@ +classes(Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true)) + ->canOnlyDependOn() + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ + // Itself + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true), + // Shared + Selector::inNamespace('CodelyTv\Shared\Domain'), + ])) + ->because('mooc domain can only import itself and shared domain'); + } + + public function test_mooc_application_should_only_import_itself_and_domain(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Application/', true)) + ->canOnlyDependOn() + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ + // Itself + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Application/', true), + Selector::inNamespace('/^CodelyTv\\\\Mooc\\\\.+\\\\Domain/', true), + // Shared + Selector::inNamespace('CodelyTv\Shared'), + ])) + ->because('mooc application can only import itself and shared'); + } + + public function test_mooc_infrastructure_should_not_import_other_contexts_beside_shared(): Rule + { + return PHPat::rule() + ->classes(Selector::inNamespace('CodelyTv\Mooc')) + ->shouldNotDependOn() + ->classes(Selector::inNamespace('CodelyTv')) + ->excluding( + // Itself + Selector::inNamespace('CodelyTv\Mooc'), + // Shared + Selector::inNamespace('CodelyTv\Shared'), + ); + } +} diff --git a/tests/Shared/Infrastructure/ArchitectureTest.php b/tests/Shared/Infrastructure/ArchitectureTest.php new file mode 100644 index 000000000..17d97198b --- /dev/null +++ b/tests/Shared/Infrastructure/ArchitectureTest.php @@ -0,0 +1,40 @@ +classes(Selector::inNamespace('CodelyTv\Shared\Domain')) ->canOnlyDependOn() - ->classes(...array_merge($this->languageClasses(), [ + ->classes(...array_merge(ArchitectureTest::languageClasses(), [ // Itself Selector::inNamespace('CodelyTv\Shared\Domain'), // Dependencies treated as domain @@ -54,23 +42,4 @@ public function test_shared_infrastructure_should_not_import_from_other_contexts Selector::classname(AuthenticateUserCommand::class), ); } - - private function languageClasses(): array - { - return [ - Selector::classname(Throwable::class), - Selector::classname(InvalidArgumentException::class), - Selector::classname(RuntimeException::class), - Selector::classname(DateTimeImmutable::class), - Selector::classname(DateTimeInterface::class), - Selector::classname(DomainException::class), - Selector::classname(Stringable::class), - Selector::classname(BackedEnum::class), - Selector::classname(Countable::class), - Selector::classname(IteratorAggregate::class), - Selector::classname(Traversable::class), - Selector::classname(ArrayIterator::class), - Selector::classname(ReflectionClass::class), - ]; - } } From b5e8459c085e8c97447acdf486f3bd3279d3d385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=20G=C3=B3mez?= Date: Tue, 3 Oct 2023 19:03:23 +0200 Subject: [PATCH 6/6] chore(phpat): test application services only have one public method --- Makefile | 2 +- tests/Shared/SharedArchitectureTest.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3581505b7..c47502189 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ lint: docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/ecs check test-architecture: - docker exec codely-php_ddd_skeleton-mooc_backend-php ./vendor/bin/phpstan analyse + docker exec codely-php_ddd_skeleton-mooc_backend-php php -d memory_limit=4G ./vendor/bin/phpstan analyse start: @if [ ! -f .env.local ]; then echo '' > .env.local; fi diff --git a/tests/Shared/SharedArchitectureTest.php b/tests/Shared/SharedArchitectureTest.php index 61a4dba64..08561532d 100644 --- a/tests/Shared/SharedArchitectureTest.php +++ b/tests/Shared/SharedArchitectureTest.php @@ -5,6 +5,8 @@ namespace CodelyTv\Tests\Shared; use CodelyTv\Backoffice\Auth\Application\Authenticate\AuthenticateUserCommand; +use CodelyTv\Shared\Domain\Bus\Event\DomainEventSubscriber; +use CodelyTv\Shared\Domain\Bus\Query\Response; use CodelyTv\Tests\Shared\Infrastructure\ArchitectureTest; use CodelyTv\Tests\Shared\Infrastructure\Doctrine\MySqlDatabaseCleaner; use PHPat\Selector\Selector; @@ -42,4 +44,18 @@ public function test_shared_infrastructure_should_not_import_from_other_contexts Selector::classname(AuthenticateUserCommand::class), ); } + + public function test_all_use_cases_can_only_have_one_public_method(): Rule + { + return PHPat::rule() + ->classes( + Selector::classname('/^CodelyTv\\\\.+\\\\.+\\\\Application\\\\.+\\\\(?!.*(?:Command|Query)$).*$/', true) + ) + ->excluding( + Selector::implements(Response::class), + Selector::implements(DomainEventSubscriber::class), + Selector::inNamespace('/.*\\\\Tests\\\\.*/', true) + ) + ->shouldHaveOnlyOnePublicMethod(); + } }