Skip to content

Commit

Permalink
Initial autoload test coverage.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaspertey committed Mar 28, 2024
1 parent 94112cb commit c79cb8b
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 77 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"php": "^8.1|^8.2|^8.3",
"illuminate/contracts": "^10.25|^11.0",
"laravel/prompts": "^0.1.16",
"lorisleiva/lody": "^0.5.0",
"spatie/laravel-package-tools": "^1.13.0"
},
"require-dev": {
Expand Down
15 changes: 2 additions & 13 deletions src/Listeners/CacheClearSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Lunarstorm\LaravelDDD\Listeners;

use ErrorException;
use Illuminate\Events\Dispatcher;
use Lunarstorm\LaravelDDD\Support\DomainAutoloader;

class CacheClearSubscriber
{
Expand All @@ -13,18 +13,7 @@ public function __construct()

public function handle(): void
{
$files = glob(base_path(config('ddd.cache_directory').'/ddd-*.php'));

foreach ($files as $file) {
try {
unlink($file);
} catch (ErrorException $exception) {
if (! str_contains($exception->getMessage(), 'No such file or directory')) {
dump($exception->getMessage());
throw $exception;
}
}
}
DomainAutoloader::clearCache();
}

/**
Expand Down
125 changes: 66 additions & 59 deletions src/Support/DomainAutoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Lorisleiva\Lody\Lody;
use Lunarstorm\LaravelDDD\Factories\DomainFactory;
use Lunarstorm\LaravelDDD\ValueObjects\DomainObject;
use ReflectionClass;
use Symfony\Component\Finder\Finder;
use Throwable;

class DomainAutoloader
Expand Down Expand Up @@ -53,65 +56,78 @@ public function autoload(): void
}
}

protected function registerDomainServiceProviders(bool|string|null $domainPath = null): void
public function registerDomainServiceProviders(bool|string|null $domainPath = null): void
{
$domainPath = is_string($domainPath) ? $domainPath : '*/*ServiceProvider.php';
// $domainPath = is_string($domainPath) ? $domainPath : '*/*ServiceProvider.php';

// $serviceProviders = $this->remember('ddd-domain-service-providers', static function () use ($domainPath) {
// return Arr::map(
// glob(base_path(DomainResolver::domainPath() . '/' . $domainPath)),
// (static function ($serviceProvider) {

// return Path::filePathToNamespace(
// $serviceProvider,
// DomainResolver::domainPath(),
// DomainResolver::domainRootNamespace()
// );
// })
// );
// });

$domainPath = app()->basePath(DomainResolver::domainPath());

if (! is_dir($domainPath)) {
return;
}

$serviceProviders = $this->remember('ddd-domain-service-providers', static function () use ($domainPath) {
return Arr::map(
glob(base_path(DomainResolver::domainPath().'/'.$domainPath)),
(static function ($serviceProvider) {

return Path::filePathToNamespace(
$serviceProvider,
DomainResolver::domainPath(),
DomainResolver::domainRootNamespace()
);
})
);
$finder = Finder::create()->files()->in($domainPath);

return Lody::classesFromFinder($finder)
->isNotAbstract()
->isInstanceOf(ServiceProvider::class)
->toArray();
});

$app = app();

foreach ($serviceProviders as $serviceProvider) {
$app->register($serviceProvider);
}
}

protected function registerDomainCommands(bool|string|null $domainPath = null): void
public function registerDomainCommands(bool|string|null $domainPath = null): void
{
$domainPath = is_string($domainPath) ? $domainPath : '*/Commands/*.php';
// $domainPath = is_string($domainPath) ? $domainPath : '*/Commands/*.php';

$domainPath = app()->basePath(DomainResolver::domainPath());

if (! is_dir($domainPath)) {
return;
}

$commands = $this->remember('ddd-domain-commands', static function () use ($domainPath) {
$commands = Arr::map(
glob(base_path(DomainResolver::domainPath().'/'.$domainPath)),
static function ($command) {
return Path::filePathToNamespace(
$command,
DomainResolver::domainPath(),
DomainResolver::domainRootNamespace()
);
}
);
$finder = Finder::create()->files()->in($domainPath);

// Filter out invalid commands (Abstract classes and classes not extending Illuminate\Console\Command)
return Arr::where($commands, static function ($command) {
if (
is_subclass_of($command, Command::class) &&
! (new ReflectionClass($command))->isAbstract()
) {
ConsoleApplication::starting(static function ($artisan) use ($command): void {
$artisan->resolve($command);
});
}
});
return Lody::classesFromFinder($finder)
->isNotAbstract()
->isInstanceOf(Command::class)
->toArray();
});
ConsoleApplication::starting(static function ($artisan) use ($commands): void {
foreach ($commands as $command) {
$artisan->resolve($command);
}

foreach ($commands as $class) {
$this->registerCommand($class);
}
}

public function registerCommand($class)
{
ConsoleApplication::starting(function ($artisan) use ($class) {
$artisan->resolve($class);
});
}

protected function registerPolicies(bool|string|null $domainPath = null): void
public function registerPolicies(bool|string|null $domainPath = null): void
{
$domainPath = is_string($domainPath) ? $domainPath : 'Policies\\{model}Policy';

Expand All @@ -136,7 +152,7 @@ protected function registerPolicies(bool|string|null $domainPath = null): void
});
}

protected function registerFactories(bool|string|null $domainPath = null): void
public function registerFactories(bool|string|null $domainPath = null): void
{
$domainPath = is_string($domainPath) ? $domainPath : 'Database\\Factories\\{model}Factory';

Expand All @@ -155,22 +171,6 @@ protected function registerFactories(bool|string|null $domainPath = null): void
});
}

protected function extractDomainAndModelFromModelNamespace(string $modelName): array
{
// Matches <DomainNamespace>\{domain}\<ModelNamespace>\{model} and extracts domain and model
// For example: Domain\Invoicing\Models\Invoice gives ['domain' => 'Invoicing', 'model' => 'Invoice']
$regex = '/'.DomainResolver::domainRootNamespace().'\\\\(?<domain>.+)\\\\'.$this->configValue('namespaces.models').'\\\\(?<model>.+)/';

if (preg_match($regex, $modelName, $matches, PREG_OFFSET_CAPTURE, 0)) {
return [
'domain' => $matches['domain'][0],
'model' => $matches['model'][0],
];
}

return [];
}

protected function remember($fileName, $callback)
{
// The cache is not available during booting, so we need to roll our own file based cache
Expand All @@ -190,6 +190,13 @@ protected function remember($fileName, $callback)
return $data;
}

public static function clearCache()
{
$files = glob(base_path(config('ddd.cache_directory').'/ddd-*.php'));

File::delete($files);
}

protected static function appNamespace()
{
try {
Expand Down
36 changes: 36 additions & 0 deletions tests/Autoload/CommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Config;
use Lunarstorm\LaravelDDD\Support\DomainAutoloader;
use Symfony\Component\Console\Exception\CommandNotFoundException;

beforeEach(function () {
Config::set('ddd.domain_path', 'src/Domain');
Config::set('ddd.domain_namespace', 'Domain');

$this->setupTestApplication();

DomainAutoloader::clearCache();
});

describe('without autoload', function () {
it('does not register the command', function () {
expect(class_exists('Domain\Invoicing\Commands\InvoiceDeliver'))->toBeTrue();
expect(fn () => Artisan::call('invoice:deliver'))->toThrow(CommandNotFoundException::class);
});
});

describe('with autoload', function () {
beforeEach(function () {
$this->afterApplicationCreated(function () {
(new DomainAutoloader())->autoload();
});
});

it('registers the command', function () {
expect(class_exists('Domain\Invoicing\Commands\InvoiceDeliver'))->toBeTrue();
Artisan::call('invoice:deliver');
expect(Artisan::output())->toContain('Invoice delivered!');
});
});
4 changes: 3 additions & 1 deletion tests/Autoload/PolicyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

Config::set('ddd.domain_namespace', 'Domain');

(new DomainAutoloader())->autoload();
$this->afterApplicationCreated(function () {
(new DomainAutoloader())->autoload();
});
});

it('can autoload domain policy', function ($class, $expectedPolicy) {
Expand Down
32 changes: 32 additions & 0 deletions tests/Autoload/ProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use Illuminate\Support\Facades\Config;
use Lunarstorm\LaravelDDD\Support\DomainAutoloader;

beforeEach(function () {
Config::set('ddd.domain_path', 'src/Domain');
Config::set('ddd.domain_namespace', 'Domain');

$this->setupTestApplication();

DomainAutoloader::clearCache();
});

describe('without autoload', function () {
it('does not register the provider', function () {
expect(fn () => app('invoicing'))->toThrow(Exception::class);
});
});

describe('with autoload', function () {
beforeEach(function () {
$this->afterApplicationCreated(function () {
(new DomainAutoloader())->autoload();
});
});

it('registers the provider', function () {
expect(app('invoicing'))->toEqual('invoicing-singleton');
$this->artisan('invoice:deliver')->expectsOutputToContain('invoice-secret');
});
});
1 change: 0 additions & 1 deletion tests/Autoload/ServiceProviderTest.php

This file was deleted.

6 changes: 3 additions & 3 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\File;
use Lunarstorm\LaravelDDD\LaravelDDDServiceProvider;
use Lunarstorm\LaravelDDD\Listeners\CacheClearSubscriber;
use Lunarstorm\LaravelDDD\Support\DomainAutoloader;
use Orchestra\Testbench\TestCase as Orchestra;
use Symfony\Component\Process\Process;

Expand All @@ -18,8 +18,6 @@ protected function setUp(): void

$this->cleanFilesAndFolders();

(new CacheClearSubscriber())->handle();

$composerFile = base_path('composer.json');
$data = json_decode(file_get_contents($composerFile), true);

Expand Down Expand Up @@ -78,6 +76,8 @@ protected function cleanFilesAndFolders()
File::deleteDirectory(base_path('Custom'));
File::deleteDirectory(base_path('src/Domain'));
File::deleteDirectory(base_path('src/Domains'));

DomainAutoloader::clearCache();
}

public function setupTestApplication()
Expand Down
24 changes: 24 additions & 0 deletions tests/resources/Domain/Invoicing/Commands/InvoiceDeliver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Domain\Invoicing\Commands;

use Domain\Invoicing\Models\Invoice;
use Illuminate\Console\Command;

class InvoiceDeliver extends Command
{
protected $signature = 'invoice:deliver';

protected $description = 'Deliver invoice.';

public function handle()
{
$this->info('Invoice delivered!');

if ($secret = Invoice::getSecret()) {
$this->line($secret);

return;
}
}
}
11 changes: 11 additions & 0 deletions tests/resources/Domain/Invoicing/Models/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@

class Invoice extends DomainModel
{
protected static $secret = null;

public static function setSecret($secret): void
{
self::$secret = $secret;
}

public static function getSecret(): ?string
{
return self::$secret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Domain\Invoicing\Providers;

use Domain\Invoicing\Models\Invoice;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class InvoiceServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('invoicing', function (Application $app) {
return 'invoicing-singleton';
});
}

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Invoice::setSecret('invoice-secret');
}
}
Loading

0 comments on commit c79cb8b

Please sign in to comment.