ldc-arcadia-api/tests/Architecture/ApplicationTest.php
Jérémy Munsch 62c221f6b6
feat(health): add oracle probe and normalize health checks
Refactor health check DTOs around a common CheckResult contract and remove the\nlegacy HealthChecks aggregate object.\n\nAdd a dedicated /api/v1/health/oracle endpoint with response mapping, OpenAPI\nschema updates, and Bruno request coverage.\n\nAlign feature, unit, integration, architecture and OpenAPI tests with the new\nset-based checks representation and make Oracle health tests deterministic in CI.\n\nUpdate command documentation to the uwil:* command namespace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-06 14:51:46 +02:00

171 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Architecture;
use Application\Health\Dto\CheckResult;
use Application\Security\Models\ValueObjects\Request\HttpMethod;
use Shared\DDD\Event\Event;
use Shared\DDD\Port\Directory;
use Shared\DDD\Port\Repository;
use Shared\DDD\Usecase\Input;
use Shared\DDD\Usecase\Output;
use Shared\DDD\Usecase\Usecase;
describe('Application Architecture Rules', function (): void {
arch('Application layer does not depend on Presentation, Infra, or App')
->expect('Application')
->not->toUse([
'Presentation',
'Infra',
'Infrastructure',
'App',
]);
arch('Application layer does not depend on Illuminate or Symfony frameworks')
->expect('Application')
->not->toUse([
'Illuminate',
'Symfony',
]);
arch('Application layer does not use Laravel helper functions')
->expect('Application')
->not->toUse([
'abort',
'app',
'auth',
'asset',
'back',
'bcrypt',
'broadcast',
'cache',
'config',
'cookie',
'csrf_token',
'dd',
'dump',
'decrypt',
'dispatch',
'event',
'factory',
'logger',
'redirect',
'request',
'response',
'route',
'session',
'storage',
'url',
'throw_if',
'throw_unless',
'__',
'trans',
'trans_choice',
'view',
]);
arch('Application layer uses strict types')
->expect('Application')
->toUseStrictTypes();
arch('Application Usecase classes are final and readonly')
->expect(function () {
$namespaces = [
'Application\Arcadia\Usecase',
'Application\Security\Usecases',
];
return array_filter(
get_declared_classes(),
fn (string $class) => ! interface_exists($class, false)
&& array_any($namespaces, fn ($ns) => str_starts_with($class, $ns))
);
})
->classes()
->toBeReadonly()
->toBeFinal();
arch('Classes implementing Shared\DDD\Usecase\Usecase are final and readonly')
->expect(Usecase::class)
->classes()
->toBeReadonly()
->toBeFinal()
->ignoring(Usecase::class);
arch('Application Input DTOs implement Shared\DDD\Usecase\Input and are final and readonly')
->expect(Input::class)
->classes()
->toBeReadonly()
->toBeFinal()
->ignoring(Input::class);
arch('Application Output DTOs implement Shared\DDD\Usecase\Output and are final and readonly')
->expect(Output::class)
->classes()
->toBeReadonly()
->toBeFinal()
->ignoring(Output::class);
arch('Application Health DTOs are final and readonly')
->expect('Application\Health\Dto')
->classes()
->toBeReadonly()
->toBeFinal()
->ignoring([
CheckResult::class,
]);
arch('Application Config classes are final and readonly')
->expect([
'Application\Arcadia\Config',
'Application\Health\Config',
'Application\Security\Config',
])
->classes()
->toBeReadonly()
->toBeFinal();
arch('Application Port Directories and Repositories contain only interfaces')
->expect([
'Application\Security\Ports\Directories',
'Application\Security\Ports\Repositories',
])
->classes()
->toBeInterface();
arch('Application Port Directories extend Shared\DDD\Port\Directory')
->expect('Application\Security\Ports\Directories')
->toExtend(Directory::class);
arch('Application Port Repositories extend Shared\DDD\Port\Repository')
->expect('Application\Security\Ports\Repositories')
->toExtend(Repository::class);
arch('Application Events implement Shared\DDD\Event\Event and are final and readonly')
->expect('Application\Security\Events')
->toImplement(Event::class)
->classes()
->toBeReadonly()
->toBeFinal();
arch('Application Security Value Objects are final and readonly')
->expect('Application\Security\Models\ValueObjects')
->classes()
->toBeReadonly()
->toBeFinal()
->ignoring([
HttpMethod::class,
]);
arch('Application layer must not contain Presenters')
->expect('Application')
->classes()
->not->toHaveSuffix('Presenter');
arch('Application layer must not contain Views')
->expect('Application')
->classes()
->not->toHaveSuffix('View');
});