From b0337f53aee81c87aefe859bdd793ccf89c52329 Mon Sep 17 00:00:00 2001 From: Jasper Tey Date: Thu, 14 Nov 2024 17:01:14 -0500 Subject: [PATCH] Working through issues sample application test environment. --- src/Commands/OptimizeCommand.php | 8 +- src/DomainManager.php | 23 ++- src/Facades/DDD.php | 1 + src/LaravelDDDServiceProvider.php | 26 ++- .../{DomainAutoloader.php => Autoloader.php} | 121 ++++++++---- src/Support/DomainMigration.php | 4 +- tests/.skeleton/config/ddd.php | 185 ++++++++++++++++++ tests/Autoload/CommandTest.php | 96 +++------ tests/Autoload/FactoryTest.php | 5 +- tests/Autoload/IgnoreTest.php | 23 +-- tests/Autoload/PolicyTest.php | 29 +-- tests/Autoload/ProviderTest.php | 118 ++++++----- tests/AutoloadingTest.php | 40 ++++ tests/BootsTestApplication.php | 5 + tests/Command/OptimizeTest.php | 33 +--- tests/Factory/DomainFactoryTest.php | 3 +- .../Model/MakeWithControllerTest.php | 14 +- tests/Support/AutoloaderTest.php | 46 ++++- tests/TestCase.php | 55 ++++++ 19 files changed, 562 insertions(+), 273 deletions(-) rename src/Support/{DomainAutoloader.php => Autoloader.php} (62%) create mode 100644 tests/.skeleton/config/ddd.php create mode 100644 tests/AutoloadingTest.php create mode 100644 tests/BootsTestApplication.php diff --git a/src/Commands/OptimizeCommand.php b/src/Commands/OptimizeCommand.php index e73462e..b36f7cd 100644 --- a/src/Commands/OptimizeCommand.php +++ b/src/Commands/OptimizeCommand.php @@ -3,7 +3,7 @@ namespace Lunarstorm\LaravelDDD\Commands; use Illuminate\Console\Command; -use Lunarstorm\LaravelDDD\Support\DomainAutoloader; +use Lunarstorm\LaravelDDD\Facades\DDD; use Lunarstorm\LaravelDDD\Support\DomainMigration; class OptimizeCommand extends Command @@ -24,11 +24,9 @@ protected function configure() public function handle() { $this->components->info('Caching DDD providers, commands, migration paths.'); - - $this->components->task('domain providers', fn () => DomainAutoloader::cacheProviders()); - $this->components->task('domain commands', fn () => DomainAutoloader::cacheCommands()); + $this->components->task('domain providers', fn () => DDD::autoloader()->cacheProviders()); + $this->components->task('domain commands', fn () => DDD::autoloader()->cacheCommands()); $this->components->task('domain migration paths', fn () => DomainMigration::cachePaths()); - $this->newLine(); } } diff --git a/src/DomainManager.php b/src/DomainManager.php index eff013d..b0f084f 100755 --- a/src/DomainManager.php +++ b/src/DomainManager.php @@ -2,6 +2,7 @@ namespace Lunarstorm\LaravelDDD; +use Lunarstorm\LaravelDDD\Support\Autoloader; use Lunarstorm\LaravelDDD\Support\GeneratorBlueprint; use Lunarstorm\LaravelDDD\Support\Path; @@ -35,19 +36,16 @@ class DomainManager protected ?GeneratorBlueprint $commandContext; - protected StubManager $stubs; - public function __construct() { $this->autoloadFilter = null; $this->applicationLayerFilter = null; $this->commandContext = null; - $this->stubs = new StubManager; } - public function config(): ConfigManager + public function autoloader(): Autoloader { - return app(ConfigManager::class); + return app(Autoloader::class); } public function composer(): ComposerManager @@ -55,6 +53,16 @@ public function composer(): ComposerManager return app(ComposerManager::class); } + public function config(): ConfigManager + { + return app(ConfigManager::class); + } + + public function stubs(): StubManager + { + return app(StubManager::class); + } + public function filterAutoloadPathsUsing(callable $filter): void { $this->autoloadFilter = $filter; @@ -94,9 +102,4 @@ public function laravelVersion($value) { return version_compare(app()->version(), $value, '>='); } - - public function stubs(): StubManager - { - return $this->stubs; - } } diff --git a/src/Facades/DDD.php b/src/Facades/DDD.php index a7c4afa..9496a08 100644 --- a/src/Facades/DDD.php +++ b/src/Facades/DDD.php @@ -10,6 +10,7 @@ * @method static void filterAutoloadPathsUsing(callable $filter) * @method static void resolveObjectSchemaUsing(callable $resolver) * @method static string packagePath(string $path = '') + * @method static \Lunarstorm\LaravelDDD\Support\Autoloader autoloader() * @method static \Lunarstorm\LaravelDDD\ConfigManager config() * @method static \Lunarstorm\LaravelDDD\StubManager stubs() * @method static \Lunarstorm\LaravelDDD\ComposerManager composer() diff --git a/src/LaravelDDDServiceProvider.php b/src/LaravelDDDServiceProvider.php index b94b48f..323c4ba 100644 --- a/src/LaravelDDDServiceProvider.php +++ b/src/LaravelDDDServiceProvider.php @@ -3,7 +3,7 @@ namespace Lunarstorm\LaravelDDD; use Illuminate\Database\Migrations\MigrationCreator; -use Lunarstorm\LaravelDDD\Support\DomainAutoloader; +use Lunarstorm\LaravelDDD\Support\Autoloader; use Lunarstorm\LaravelDDD\Support\DomainMigration; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -16,15 +16,27 @@ public function configurePackage(Package $package): void return new DomainManager; }); - $this->app->scoped(ConfigManager::class, function () { - return new ConfigManager(config_path('ddd.php')); + $this->app->scoped(Autoloader::class, function () { + return new Autoloader; }); $this->app->scoped(ComposerManager::class, function () { return ComposerManager::make(app()->basePath('composer.json')); }); + $this->app->scoped(ConfigManager::class, function () { + return new ConfigManager(config_path('ddd.php')); + }); + + $this->app->scoped(StubManager::class, function () { + return new StubManager; + }); + $this->app->bind('ddd', DomainManager::class); + $this->app->bind('ddd.autoloader', Autoloader::class); + $this->app->bind('ddd.config', ConfigManager::class); + $this->app->bind('ddd.composer', ComposerManager::class); + $this->app->bind('ddd.stubs', StubManager::class); /* * This class is a Package Service Provider @@ -120,12 +132,16 @@ public function packageBooted() key: 'laravel-ddd', ); } + + // dump([ + // 'package booted' => config('ddd') + // ]); + + app('ddd.autoloader')->boot(); } public function packageRegistered() { - (new DomainAutoloader)->autoload(); - $this->registerMigrations(); } } diff --git a/src/Support/DomainAutoloader.php b/src/Support/Autoloader.php similarity index 62% rename from src/Support/DomainAutoloader.php rename to src/Support/Autoloader.php index f879740..c874e93 100644 --- a/src/Support/DomainAutoloader.php +++ b/src/Support/Autoloader.php @@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; +use Illuminate\Support\Traits\Conditionable; use Lorisleiva\Lody\Lody; use Lunarstorm\LaravelDDD\Factories\DomainFactory; use Lunarstorm\LaravelDDD\ValueObjects\DomainObject; @@ -19,42 +20,51 @@ use Symfony\Component\Finder\SplFileInfo; use Throwable; -class DomainAutoloader +class Autoloader { + use Conditionable; + + protected string $appNamespace; + + protected array $discoveredCommands = []; + + protected array $discoveredProviders = []; + public function __construct() { - // + $this->appNamespace = $this->resolveAppNamespace(); } - public function autoload(): void + public function boot(): void { if (! config()->has('ddd.autoload')) { return; } - $this->handleProviders(); - - if (app()->runningInConsole()) { - $this->handleCommands(); - } + // if ($discoveredCommands = $this->getDiscoveredCommands()) { + // dump('Commands were already discovered', $discoveredCommands); + // } - if (config('ddd.autoload.policies') === true) { - $this->handlePolicies(); - } + // if ($discoveredProviders = $this->getDiscoveredProviders()) { + // dump('Providers were already discovered', $discoveredProviders); + // } - if (config('ddd.autoload.factories') === true) { - $this->handleFactories(); - } + $this + ->handleProviders() + ->when(app()->runningInConsole(), fn ($autoloader) => $autoloader->handleProviders()) + ->when(config('ddd.autoload.commands') === true, fn ($autoloader) => $autoloader->handleCommands()) + ->when(config('ddd.autoload.policies') === true, fn ($autoloader) => $autoloader->handlePolicies()) + ->when(config('ddd.autoload.factories') === true, fn ($autoloader) => $autoloader->handleFactories()); } - protected static function normalizePaths($path): array + protected function normalizePaths($path): array { return collect($path) ->filter(fn ($path) => is_dir($path)) ->toArray(); } - protected static function getAllLayerPaths(): array + public function getAllLayerPaths(): array { return collect([ DomainResolver::domainPath(), @@ -63,33 +73,49 @@ protected static function getAllLayerPaths(): array ])->map(fn ($path) => app()->basePath($path))->toArray(); } - protected static function getCustomLayerPaths(): array + protected function getCustomLayerPaths(): array { return collect([ ...array_values(config('ddd.layers', [])), ])->map(fn ($path) => app()->basePath($path))->toArray(); } - protected function handleProviders(): void + protected function handleProviders() { $providers = DomainCache::has('domain-providers') ? DomainCache::get('domain-providers') - : static::discoverProviders(); + : $this->discoverProviders(); foreach ($providers as $provider) { + $this->discoveredProviders[$provider] = $provider; app()->register($provider); } + + return $this; } - protected function handleCommands(): void + protected function handleCommands() { $commands = DomainCache::has('domain-commands') ? DomainCache::get('domain-commands') - : static::discoverCommands(); + : $this->discoverCommands(); foreach ($commands as $command) { + $this->discoveredCommands[$command] = $command; $this->registerCommand($command); } + + return $this; + } + + public function getDiscoveredCommands(): array + { + return $this->discoveredCommands; + } + + public function getDiscoveredProviders(): array + { + return $this->discoveredProviders; } protected function registerCommand($class) @@ -99,7 +125,7 @@ protected function registerCommand($class) }); } - protected function handlePolicies(): void + protected function handlePolicies() { Gate::guessPolicyNamesUsing(static function (string $class): array|string { if ($model = DomainObject::fromClass($class, 'model')) { @@ -120,26 +146,28 @@ protected function handlePolicies(): void return class_exists($class); }) ?: [$classDirname.'\\Policies\\'.class_basename($class).'Policy']); }); + + return $this; } - protected function handleFactories(): void + protected function handleFactories() { Factory::guessFactoryNamesUsing(function (string $modelName) { if ($factoryName = DomainFactory::resolveFactoryName($modelName)) { return $factoryName; } - $appNamespace = static::appNamespace(); - - $modelName = Str::startsWith($modelName, $appNamespace.'Models\\') - ? Str::after($modelName, $appNamespace.'Models\\') - : Str::after($modelName, $appNamespace); + $modelName = Str::startsWith($modelName, $this->appNamespace.'Models\\') + ? Str::after($modelName, $this->appNamespace.'Models\\') + : Str::after($modelName, $this->appNamespace); return 'Database\\Factories\\'.$modelName.'Factory'; }); + + return $this; } - protected static function finder($paths) + protected function finder($paths) { $filter = app('ddd')->getAutoloadFilter() ?? function (SplFileInfo $file) { $pathAfterDomain = str($file->getRelativePath()) @@ -161,7 +189,7 @@ protected static function finder($paths) ->filter($filter); } - protected static function discoverProviders(): array + protected function discoverProviders(): array { $configValue = config('ddd.autoload.providers'); @@ -169,9 +197,9 @@ protected static function discoverProviders(): array return []; } - $paths = static::normalizePaths( + $paths = $this->normalizePaths( $configValue === true - ? static::getAllLayerPaths() + ? $this->getAllLayerPaths() : $configValue ); @@ -179,13 +207,13 @@ protected static function discoverProviders(): array return []; } - return Lody::classesFromFinder(static::finder($paths)) + return Lody::classesFromFinder($this->finder($paths)) ->isNotAbstract() ->isInstanceOf(ServiceProvider::class) ->toArray(); } - protected static function discoverCommands(): array + protected function discoverCommands(): array { $configValue = config('ddd.autoload.commands'); @@ -193,9 +221,9 @@ protected static function discoverCommands(): array return []; } - $paths = static::normalizePaths( + $paths = $this->normalizePaths( $configValue === true ? - static::getAllLayerPaths() + $this->getAllLayerPaths() : $configValue ); @@ -203,23 +231,32 @@ protected static function discoverCommands(): array return []; } - return Lody::classesFromFinder(static::finder($paths)) + return Lody::classesFromFinder($this->finder($paths)) ->isNotAbstract() ->isInstanceOf(Command::class) ->toArray(); } - public static function cacheProviders(): void + public function cacheProviders() + { + DomainCache::set('domain-providers', $this->discoverProviders()); + + return $this; + } + + public function cacheCommands() { - DomainCache::set('domain-providers', static::discoverProviders()); + DomainCache::set('domain-commands', $this->discoverCommands()); + + return $this; } - public static function cacheCommands(): void + protected function flush() { - DomainCache::set('domain-commands', static::discoverCommands()); + return $this; } - protected static function appNamespace() + protected function resolveAppNamespace() { try { return Container::getInstance() diff --git a/src/Support/DomainMigration.php b/src/Support/DomainMigration.php index 9f3a939..b6cd0ba 100644 --- a/src/Support/DomainMigration.php +++ b/src/Support/DomainMigration.php @@ -31,7 +31,7 @@ public static function paths(): array : static::discoverPaths(); } - protected static function normalizePaths($path): array + protected static function filterDirectories($path): array { return collect($path) ->filter(fn ($path) => is_dir($path)) @@ -46,7 +46,7 @@ public static function discoverPaths(): array return []; } - $paths = static::normalizePaths([ + $paths = static::filterDirectories([ app()->basePath(DomainResolver::domainPath()), ]); diff --git a/tests/.skeleton/config/ddd.php b/tests/.skeleton/config/ddd.php new file mode 100644 index 0000000..9f65237 --- /dev/null +++ b/tests/.skeleton/config/ddd.php @@ -0,0 +1,185 @@ + 'src/Domain', + 'domain_namespace' => 'Domain', + + /* + |-------------------------------------------------------------------------- + | Application Layer + |-------------------------------------------------------------------------- + | + | The path and namespace of the application layer, and the objects + | that should be recognized as part of the application layer. + | + */ + 'application_path' => 'Application', + 'application_namespace' => 'src\Application', + 'application_objects' => [ + 'controller', + 'request', + 'middleware', + ], + + /* + |-------------------------------------------------------------------------- + | Custom Layers + |-------------------------------------------------------------------------- + | + | Additional top-level namespaces and paths that should be recognized as + | layers when generating ddd:* objects. + | + | e.g., 'Infrastructure' => 'src/Infrastructure', + | + */ + 'layers' => [ + 'Infrastructure' => 'src/Infrastructure', + ], + + /* + |-------------------------------------------------------------------------- + | Object Namespaces + |-------------------------------------------------------------------------- + | + | This value contains the default namespaces of ddd:* generated + | objects relative to the layer of which the object belongs to. + | + */ + 'namespaces' => [ + 'model' => 'Models', + 'data_transfer_object' => 'Data', + 'view_model' => 'ViewModels', + 'value_object' => 'ValueObjects', + 'action' => 'Actions', + 'cast' => 'Casts', + 'class' => '', + 'channel' => 'Channels', + 'command' => 'Commands', + 'controller' => 'Controllers', + 'enum' => 'Enums', + 'event' => 'Events', + 'exception' => 'Exceptions', + 'factory' => 'Database\Factories', + 'interface' => '', + 'job' => 'Jobs', + 'listener' => 'Listeners', + 'mail' => 'Mail', + 'middleware' => 'Middleware', + 'migration' => 'Database\Migrations', + 'notification' => 'Notifications', + 'observer' => 'Observers', + 'policy' => 'Policies', + 'provider' => 'Providers', + 'resource' => 'Resources', + 'request' => 'Requests', + 'rule' => 'Rules', + 'scope' => 'Scopes', + 'seeder' => 'Database\Seeders', + 'trait' => '', + ], + + /* + |-------------------------------------------------------------------------- + | Base Model + |-------------------------------------------------------------------------- + | + | The base model class which generated domain models should extend. If + | set to null, the generated models will extend Laravel's default. + | + */ + 'base_model' => null, + + /* + |-------------------------------------------------------------------------- + | Base DTO + |-------------------------------------------------------------------------- + | + | The base class which generated data transfer objects should extend. By + | default, generated DTOs will extend `Spatie\LaravelData\Data` from + | Spatie's Laravel-data package, a highly recommended data object + | package to work with. + | + */ + 'base_dto' => 'Spatie\LaravelData\Data', + + /* + |-------------------------------------------------------------------------- + | Base ViewModel + |-------------------------------------------------------------------------- + | + | The base class which generated view models should extend. By default, + | generated domain models will extend `Domain\Shared\ViewModels\BaseViewModel`, + | which will be created if it doesn't already exist. + | + */ + 'base_view_model' => 'Domain\Shared\ViewModels\ViewModel', + + /* + |-------------------------------------------------------------------------- + | Base Action + |-------------------------------------------------------------------------- + | + | The base class which generated action objects should extend. By default, + | generated actions are based on the `lorisleiva/laravel-actions` package + | and do not extend anything. + | + */ + 'base_action' => null, + + /* + |-------------------------------------------------------------------------- + | Autoloading + |-------------------------------------------------------------------------- + | + | Configure whether domain providers, commands, policies, factories, + | and migrations should be auto-discovered and registered. + | + */ + 'autoload' => [ + 'providers' => true, + 'commands' => true, + 'policies' => true, + 'factories' => true, + 'migrations' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Autoload Ignore Folders + |-------------------------------------------------------------------------- + | + | Folders that should be skipped during autoloading discovery, + | relative to the root of each domain. + | + | e.g., src/Domain/Invoicing/ + | + | If more advanced filtering is needed, a callback can be registered + | using `DDD::filterAutoloadPathsUsing(callback $filter)` in + | the AppServiceProvider's boot method. + | + */ + 'autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + + /* + |-------------------------------------------------------------------------- + | Caching + |-------------------------------------------------------------------------- + | + | The folder where the domain cache files will be stored. Used for domain + | autoloading. + | + */ + 'cache_directory' => 'bootstrap/cache/ddd', +]; diff --git a/tests/Autoload/CommandTest.php b/tests/Autoload/CommandTest.php index 4fb976b..d62e31b 100644 --- a/tests/Autoload/CommandTest.php +++ b/tests/Autoload/CommandTest.php @@ -1,31 +1,14 @@ 'src/Domain', - 'ddd.domain_namespace' => 'Domain', - 'ddd.application_namespace' => 'Application', - 'ddd.application_path' => 'src/Application', - 'ddd.application_objects' => [ - 'controller', - 'request', - 'middleware', - ], - 'ddd.layers' => [ - 'Infrastructure' => 'src/Infrastructure', - ], - 'ddd.autoload_ignore' => [ - 'Tests', - 'Database/Migrations', - ], - 'cache.default' => 'file', - ]); + $this->setupTestApplication(); }); afterEach(function () { @@ -33,17 +16,11 @@ }); describe('without autoload', function () { - beforeEach(function () { - Config::set('ddd.autoload.commands', false); - - $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); - }); - - $this->setupTestApplication(); - }); - it('does not register the command', function ($className, $command) { + $this->refreshApplicationWithConfig([ + 'ddd.autoload.commands' => false, + ]); + expect(class_exists($className))->toBeTrue(); expect(fn () => Artisan::call($command))->toThrow(CommandNotFoundException::class); })->with([ @@ -54,19 +31,17 @@ }); describe('with autoload', function () { - beforeEach(function () { - Config::set('ddd.autoload.commands', true); - - $this->setupTestApplication(); - }); - it('registers existing commands', function ($className, $command, $output) { - expect(class_exists($className))->toBeTrue(); - $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.commands' => true, + ]); + + expect(class_exists($className))->toBeTrue(); + expect(collect(Artisan::all())) ->has($command) ->toBeTrue(); @@ -78,45 +53,20 @@ ['Infrastructure\Commands\LogPrune', 'log:prune', 'System logs pruned!'], ['Application\Commands\ApplicationSync', 'application:sync', 'Application state synced!'], ]); - - it('registers newly created commands', function () { - $command = 'app:invoice-void'; - - $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); - }); - - expect(collect(Artisan::all())) - ->has($command) - ->toBeFalse(); - - Artisan::call('ddd:command', [ - 'name' => 'InvoiceVoid', - '--domain' => 'Invoicing', - ]); - - expect(collect(Artisan::all())) - ->has($command) - ->toBeTrue(); - - $this->artisan($command)->assertSuccessful(); - })->skip("Can't get this to work, might not be test-able without a real app environment."); }); describe('caching', function () { - beforeEach(function () { - Config::set('ddd.autoload.commands', true); - - $this->setupTestApplication(); - }); - it('remembers the last cached state', function () { DomainCache::set('domain-commands', []); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.commands' => true, + ]); + // commands should not be recognized due to cached empty-state expect(fn () => Artisan::call('invoice:deliver'))->toThrow(CommandNotFoundException::class); expect(fn () => Artisan::call('log:prune'))->toThrow(CommandNotFoundException::class); @@ -128,9 +78,13 @@ DomainCache::clear(); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.commands' => true, + ]); + $this->artisan('invoice:deliver')->assertSuccessful(); $this->artisan('log:prune')->assertSuccessful(); $this->artisan('application:sync')->assertSuccessful(); diff --git a/tests/Autoload/FactoryTest.php b/tests/Autoload/FactoryTest.php index f496b38..79a4a4e 100644 --- a/tests/Autoload/FactoryTest.php +++ b/tests/Autoload/FactoryTest.php @@ -2,7 +2,6 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; -use Lunarstorm\LaravelDDD\Support\DomainAutoloader; beforeEach(function () { $this->setupTestApplication(); @@ -15,7 +14,7 @@ Config::set('ddd.autoload.factories', true); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); }); @@ -52,7 +51,7 @@ Config::set('ddd.autoload.factories', false); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); }); diff --git a/tests/Autoload/IgnoreTest.php b/tests/Autoload/IgnoreTest.php index f7c82fd..183147b 100644 --- a/tests/Autoload/IgnoreTest.php +++ b/tests/Autoload/IgnoreTest.php @@ -5,29 +5,12 @@ use Illuminate\Support\Str; use Lunarstorm\LaravelDDD\Facades\DDD; use Lunarstorm\LaravelDDD\Support\DomainCache; +use Lunarstorm\LaravelDDD\Tests\BootsTestApplication; use Symfony\Component\Finder\SplFileInfo; -beforeEach(function () { - Config::set([ - 'ddd.domain_path' => 'src/Domain', - 'ddd.domain_namespace' => 'Domain', - 'ddd.application_namespace' => 'Application', - 'ddd.application_path' => 'src/Application', - 'ddd.application_objects' => [ - 'controller', - 'request', - 'middleware', - ], - 'ddd.layers' => [ - 'Infrastructure' => 'src/Infrastructure', - ], - 'ddd.autoload_ignore' => [ - 'Tests', - 'Database/Migrations', - ], - 'cache.default' => 'file', - ]); +uses(BootsTestApplication::class); +beforeEach(function () { $this->setupTestApplication(); }); diff --git a/tests/Autoload/PolicyTest.php b/tests/Autoload/PolicyTest.php index a9e560e..168ab4c 100644 --- a/tests/Autoload/PolicyTest.php +++ b/tests/Autoload/PolicyTest.php @@ -1,35 +1,12 @@ setupTestApplication(); - - Config::set([ - 'ddd.domain_path' => 'src/Domain', - 'ddd.domain_namespace' => 'Domain', - 'ddd.application_namespace' => 'Application', - 'ddd.application_path' => 'src/Application', - 'ddd.application_objects' => [ - 'controller', - 'request', - 'middleware', - ], - 'ddd.layers' => [ - 'Infrastructure' => 'src/Infrastructure', - ], - 'ddd.autoload_ignore' => [ - 'Tests', - 'Database/Migrations', - ], - 'cache.default' => 'file', - ]); - - $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); - }); }); it('can autoload policy', function ($class, $expectedPolicy) { diff --git a/tests/Autoload/ProviderTest.php b/tests/Autoload/ProviderTest.php index 73c8ccb..57bcacd 100644 --- a/tests/Autoload/ProviderTest.php +++ b/tests/Autoload/ProviderTest.php @@ -1,102 +1,116 @@ 'src/Domain', - 'ddd.domain_namespace' => 'Domain', - 'ddd.application_namespace' => 'Application', - 'ddd.application_path' => 'src/Application', - 'ddd.application_objects' => [ - 'controller', - 'request', - 'middleware', - ], - 'ddd.layers' => [ - 'Infrastructure' => 'src/Infrastructure', - ], - 'ddd.autoload_ignore' => [ - 'Tests', - 'Database/Migrations', - ], - 'cache.default' => 'file', - ]); + // $this->refreshApplicationWithConfig([ + // 'ddd.domain_path' => 'src/Domain', + // 'ddd.domain_namespace' => 'Domain', + // 'ddd.application_namespace' => 'Application', + // 'ddd.application_path' => 'src/Application', + // 'ddd.application_objects' => [ + // 'controller', + // 'request', + // 'middleware', + // ], + // 'ddd.layers' => [ + // 'Infrastructure' => 'src/Infrastructure', + // ], + // 'ddd.autoload_ignore' => [ + // 'Tests', + // 'Database/Migrations', + // ], + // 'cache.default' => 'file', + // ]); $this->setupTestApplication(); }); -afterEach(function () { - $this->setupTestApplication(); -}); +// afterEach(function () { +// $this->setupTestApplication(); +// }); describe('without autoload', function () { - beforeEach(function () { - config([ + it('does not register the provider', function ($binding) { + // setConfigValues([ + // 'ddd.autoload.providers' => false, + // ]); + + $this->afterApplicationRefreshed(function () { + app('ddd.autoloader')->boot(); + }); + + $this->refreshApplicationWithConfig([ 'ddd.autoload.providers' => false, ]); - (new DomainAutoloader)->autoload(); - }); + expect(DDD::autoloader()->getDiscoveredProviders())->toBeEmpty(); - it('does not register the provider', function () { - expect(fn () => app('invoicing'))->toThrow(Exception::class); - }); + expect(fn () => app($binding))->toThrow(Exception::class); + })->with([ + ['invoicing'], + ['application-layer'], + ['infrastructure-layer'], + ]); }); describe('with autoload', function () { - beforeEach(function () { - config([ - 'ddd.autoload.providers' => true, - ]); - }); - it('registers the provider in domain layer', function () { $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.providers' => true, + ]); + expect(app('invoicing'))->toEqual('invoicing-singleton'); $this->artisan('invoice:deliver')->expectsOutputToContain('invoice-secret'); }); it('registers the provider in application layer', function () { $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.providers' => true, + ]); + expect(app('application-layer'))->toEqual('application-layer-singleton'); $this->artisan('application:sync')->expectsOutputToContain('application-secret'); }); it('registers the provider in custom layer', function () { $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.providers' => true, + ]); + expect(app('infrastructure-layer'))->toEqual('infrastructure-layer-singleton'); $this->artisan('log:prune')->expectsOutputToContain('infrastructure-secret'); }); }); describe('caching', function () { - beforeEach(function () { - config([ - 'ddd.autoload.providers' => true, - ]); - - $this->setupTestApplication(); - }); - it('remembers the last cached state', function () { DomainCache::set('domain-providers', []); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.providers' => true, + ]); + expect(fn () => app('invoicing'))->toThrow(Exception::class); expect(fn () => app('application-layer'))->toThrow(Exception::class); expect(fn () => app('infrastructure-layer'))->toThrow(Exception::class); @@ -107,9 +121,13 @@ DomainCache::clear(); $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); + $this->refreshApplicationWithConfig([ + 'ddd.autoload.providers' => true, + ]); + expect(app('invoicing'))->toEqual('invoicing-singleton'); $this->artisan('invoice:deliver')->expectsOutputToContain('invoice-secret'); diff --git a/tests/AutoloadingTest.php b/tests/AutoloadingTest.php new file mode 100644 index 0000000..8ed1eab --- /dev/null +++ b/tests/AutoloadingTest.php @@ -0,0 +1,40 @@ + 'src/Domain', + // 'ddd.domain_namespace' => 'Domain', + // 'ddd.application_namespace' => 'Application', + // 'ddd.application_path' => 'src/Application', + // 'ddd.application_objects' => [ + // 'controller', + // 'request', + // 'middleware', + // ], + // 'ddd.layers' => [ + // 'Infrastructure' => 'src/Infrastructure', + // ], + // 'ddd.autoload_ignore' => [ + // 'Tests', + // 'Database/Migrations', + // ], + // 'cache.default' => 'file', + // ...static::$configValues, + // ]; + + // tap($app['config'], function (Repository $config) { + // foreach (static::$configValues as $key => $value) { + // $config->set($key, $value); + // } + // }); + // } +} diff --git a/tests/BootsTestApplication.php b/tests/BootsTestApplication.php new file mode 100644 index 0000000..d653a3d --- /dev/null +++ b/tests/BootsTestApplication.php @@ -0,0 +1,5 @@ +setupTestApplication(); - Config::set([ - 'ddd.domain_path' => 'src/Domain', - 'ddd.domain_namespace' => 'Domain', - 'ddd.application_namespace' => 'Application', - 'ddd.application_path' => 'src/Application', - 'ddd.application_objects' => [ - 'controller', - 'request', - 'middleware', - ], - 'ddd.layers' => [ - 'Infrastructure' => 'src/Infrastructure', - ], - 'ddd.autoload_ignore' => [ - 'Tests', - 'Database/Migrations', - ], - 'cache.default' => 'file', - ]); - $this->artisan('optimize:clear')->execute(); DomainCache::clear(); @@ -45,10 +26,6 @@ expect(DomainCache::get('domain-commands'))->toBeNull(); expect(DomainCache::get('domain-migration-paths'))->toBeNull(); - // $this->afterApplicationCreated(function () { - // (new DomainAutoloader)->autoload(); - // }); - $this ->artisan('ddd:optimize') ->expectsOutputToContain('Caching DDD providers, commands, migration paths.') @@ -113,8 +90,6 @@ describe('laravel optimize', function () { test('optimize will include ddd:optimize', function () { - config(['cache.default' => 'file']); - expect(DomainCache::get('domain-providers'))->toBeNull(); expect(DomainCache::get('domain-commands'))->toBeNull(); expect(DomainCache::get('domain-migration-paths'))->toBeNull(); @@ -127,8 +102,6 @@ }); test('optimize:clear will clear ddd cache', function () { - config(['cache.default' => 'file']); - $this->artisan('ddd:optimize')->execute(); expect(DomainCache::get('domain-providers'))->not->toBeNull(); diff --git a/tests/Factory/DomainFactoryTest.php b/tests/Factory/DomainFactoryTest.php index 82e6e54..78f76a7 100644 --- a/tests/Factory/DomainFactoryTest.php +++ b/tests/Factory/DomainFactoryTest.php @@ -5,7 +5,6 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; use Lunarstorm\LaravelDDD\Factories\DomainFactory; -use Lunarstorm\LaravelDDD\Support\DomainAutoloader; it('can resolve the factory name of a domain model', function ($modelClass, $expectedFactoryClass) { $this->setupTestApplication(); @@ -29,7 +28,7 @@ it('can instantiate a domain model factory', function ($domainParameter, $modelName, $modelClass) { $this->afterApplicationCreated(function () { - (new DomainAutoloader)->autoload(); + app('ddd.autoloader')->boot(); }); $this->setupTestApplication(); diff --git a/tests/Generator/Model/MakeWithControllerTest.php b/tests/Generator/Model/MakeWithControllerTest.php index c5f1077..2418ad3 100644 --- a/tests/Generator/Model/MakeWithControllerTest.php +++ b/tests/Generator/Model/MakeWithControllerTest.php @@ -1,15 +1,21 @@ 'src/Domain', + 'ddd.domain_namespace' => 'Domain', + 'ddd.base_model' => DomainModel::class, + 'ddd.application_namespace' => 'App\Modules', + 'ddd.application_path' => 'app/Modules', + 'ddd.application_objects' => [ + 'controller', + ], + ]); $this->setupTestApplication(); }); diff --git a/tests/Support/AutoloaderTest.php b/tests/Support/AutoloaderTest.php index aec470b..2ee1ddf 100644 --- a/tests/Support/AutoloaderTest.php +++ b/tests/Support/AutoloaderTest.php @@ -1,13 +1,53 @@ setupTestApplication(); }); it('can run', function () { - $autoloader = new DomainAutoloader; + $autoloader = new Autoloader; - $autoloader->autoload(); + $autoloader->boot(); })->throwsNoExceptions(); + +beforeEach(function () { + config([ + 'ddd.domain_path' => 'src/Domain', + 'ddd.domain_namespace' => 'Domain', + 'ddd.application_namespace' => 'Application', + 'ddd.application_path' => 'src/Application', + 'ddd.application_objects' => [ + 'controller', + 'request', + 'middleware', + ], + 'ddd.layers' => [ + 'Infrastructure' => 'src/Infrastructure', + 'Support' => 'src/Support', + 'Library' => 'lib', + ], + 'ddd.autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + 'cache.default' => 'file', + ]); + + $this->setupTestApplication(); +}); + +it('can discover paths to all layers', function () { + $autoloader = new Autoloader; + + $expected = [ + app()->basePath('src/Domain'), + app()->basePath('src/Application'), + app()->basePath('src/Infrastructure'), + app()->basePath('src/Support'), + app()->basePath('lib'), + ]; + + expect($autoloader->getAllLayerPaths())->toEqualCanonicalizing($expected); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php index 85e74a6..5fdb780 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -14,6 +14,8 @@ class TestCase extends Orchestra { public static $configValues = []; + public $appConfig = []; + protected function setUp(): void { $this->afterApplicationCreated(function () { @@ -36,15 +38,67 @@ public static function configValues(array $values) static::$configValues = $values; } + public static function resetConfig() + { + static::$configValues = []; + } + + protected function defineConfigBeforeEnvironment() {} + protected function defineEnvironment($app) { + if (in_array(BootsTestApplication::class, class_uses_recursive($this))) { + static::$configValues = [ + 'ddd.domain_path' => 'src/Domain', + 'ddd.domain_namespace' => 'Domain', + 'ddd.application_namespace' => 'Application', + 'ddd.application_path' => 'src/Application', + 'ddd.application_objects' => [ + 'controller', + 'request', + 'middleware', + ], + 'ddd.layers' => [ + 'Infrastructure' => 'src/Infrastructure', + ], + 'ddd.autoload_ignore' => [ + 'Tests', + 'Database/Migrations', + ], + 'cache.default' => 'file', + ...static::$configValues, + ]; + } + tap($app['config'], function (Repository $config) { foreach (static::$configValues as $key => $value) { $config->set($key, $value); } + + foreach ($this->appConfig as $key => $value) { + $config->set($key, $value); + } }); } + protected function refreshApplicationWithConfig(array $config) + { + $this->appConfig = $config; + + $this->refreshApplication(); + + $this->afterApplicationRefreshed(fn () => $this->appConfig = []); + + return $this; + } + + protected function withConfig(array $config) + { + $this->appConfig = $config; + + return $this; + } + protected function getComposerFileContents() { return file_get_contents(base_path('composer.json')); @@ -161,6 +215,7 @@ protected function setupTestApplication() File::copyDirectory(__DIR__.'/.skeleton/database', base_path('database')); File::copyDirectory(__DIR__.'/.skeleton/src', base_path('src')); File::copy(__DIR__.'/.skeleton/bootstrap/providers.php', base_path('bootstrap/providers.php')); + // File::copy(__DIR__ . '/.skeleton/config/ddd.php', config_path('ddd.php')); $this->setAutoloadPathInComposer('Domain', 'src/Domain'); $this->setAutoloadPathInComposer('Application', 'src/Application');