266 lines
8.4 KiB
PHP
266 lines
8.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Uwil\ArcadiaApiPhpClient\Auth\ApiMandatoryHeadersMiddleware;
|
|
use Uwil\ArcadiaApiPhpClient\Auth\ClientIdentifier;
|
|
use Uwil\ArcadiaApiPhpClient\Auth\ClientSecret;
|
|
use Uwil\ArcadiaApiPhpClient\Auth\Nonce;
|
|
use Uwil\ArcadiaApiPhpClient\Error\AbstractApiError;
|
|
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\PortionType;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\AbstractIncoCompoPartDto;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\IncoCompoPartDto;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSortColumn;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\IncoSortColumn;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\SortColumn;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\LibMiddleware;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\Pagination\PageNumber;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\Pagination\PageSize;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\Pagination\Pagination;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
|
use Uwil\ArcadiaApiPhpClient\Logger\ApiLoggerMiddleware;
|
|
use Uwil\ArcadiaApiPhpClient\Repository\AbstractApiRepository;
|
|
use Uwil\ArcadiaApiPhpClient\Repository\Repository;
|
|
|
|
$libraryNamespace = 'Uwil\ArcadiaApiPhpClient';
|
|
|
|
describe('coding standards', function () use ($libraryNamespace): void {
|
|
arch()->preset()->php();
|
|
arch()->preset()->security();
|
|
|
|
test('no shell execution functions are used')
|
|
->expect(['exec', 'shell_exec', 'system', 'passthru', 'proc_open', 'popen', 'pcntl_exec'])
|
|
->not->toBeUsed();
|
|
|
|
test('no eval usage')
|
|
->expect(['eval'])
|
|
->not->toBeUsed();
|
|
|
|
test('no dangerous file functions')
|
|
->expect(['file_put_contents'])
|
|
->not->toBeUsed();
|
|
|
|
test('no globals are used')
|
|
->expect(['$GLOBALS', 'global'])
|
|
->not->toBeUsed();
|
|
|
|
test('no singletons')
|
|
->expect(['getInstance', 'instance'])
|
|
->not->toBeUsed();
|
|
|
|
test('no insecure hashing')
|
|
->expect(['md5', 'sha1'])
|
|
->not->toBeUsed();
|
|
|
|
test('no sleep function are used')
|
|
->expect(['sleep', 'usleep', 'time_nanosleep', 'time_sleep_until'])
|
|
->not->toBeUsed();
|
|
|
|
test('no debug globals are used')
|
|
->expect(['var_dump', 'die', 'print_r', 'dd', 'dump'])
|
|
->not->toBeUsed();
|
|
|
|
arch('all source files use strict types')
|
|
->expect($libraryNamespace)
|
|
->toUseStrictTypes();
|
|
});
|
|
|
|
describe('naming conventions', function () use ($libraryNamespace): void {
|
|
arch('mappers have Mapper suffix')
|
|
->expect("{$libraryNamespace}\Mapper")
|
|
->toHaveSuffix('Mapper');
|
|
|
|
arch('repositories have Repository suffix')
|
|
->expect("{$libraryNamespace}\Repository")
|
|
->toHaveSuffix('Repository');
|
|
|
|
arch('services have Service suffix')
|
|
->expect("{$libraryNamespace}\Service")
|
|
->toHaveSuffix('Service');
|
|
|
|
arch('errors have Error suffix')
|
|
->expect("{$libraryNamespace}\Error")
|
|
->toHaveSuffix('Error');
|
|
|
|
arch('factories have Factory suffix')
|
|
->expect("{$libraryNamespace}\Factory")
|
|
->toHaveSuffix('Factory');
|
|
|
|
arch('middlewares have Middleware suffix')
|
|
->expect([
|
|
ApiMandatoryHeadersMiddleware::class,
|
|
ApiLoggerMiddleware::class,
|
|
])
|
|
->toHaveSuffix('Middleware');
|
|
});
|
|
|
|
describe('inheritance contracts', function () use ($libraryNamespace): void {
|
|
arch('ApiDtos implement Dto')
|
|
->expect("{$libraryNamespace}\Lib\ApiDto\Dto")
|
|
->toImplement(Dto::class)
|
|
->ignoring([
|
|
Dto::class,
|
|
AbstractIncoCompoPartDto::class,
|
|
IncoCompoPartDto::class,
|
|
]);
|
|
|
|
arch('Errors implement ApiError')
|
|
->expect("{$libraryNamespace}\Error")
|
|
->toImplement(ApiError::class)
|
|
->ignoring([ApiError::class]);
|
|
|
|
arch('Concrete errors extend AbstractApiError')
|
|
->expect("{$libraryNamespace}\Error")
|
|
->classes()
|
|
->toExtend(AbstractApiError::class)
|
|
->ignoring([AbstractApiError::class]);
|
|
|
|
arch('Repositories implement Repository')
|
|
->expect("{$libraryNamespace}\Repository")
|
|
->toImplement(Repository::class)
|
|
->ignoring([Repository::class]);
|
|
|
|
arch('Concrete repositories extend AbstractApiRepository')
|
|
->expect("{$libraryNamespace}\Repository")
|
|
->classes()
|
|
->toExtend(AbstractApiRepository::class)
|
|
->ignoring([AbstractApiRepository::class]);
|
|
|
|
arch('Middlewares implement LibMiddleware')
|
|
->expect([
|
|
ApiMandatoryHeadersMiddleware::class,
|
|
ApiLoggerMiddleware::class,
|
|
])
|
|
->toImplement(LibMiddleware::class);
|
|
|
|
arch('Sort columns implement SortColumn')
|
|
->expect([
|
|
EmployeeSortColumn::class,
|
|
IncoSortColumn::class,
|
|
])
|
|
->toImplement(SortColumn::class);
|
|
|
|
arch('Value objects implement ValueObjectId')
|
|
->expect("{$libraryNamespace}\Lib\ApiValueObject")
|
|
->toImplement(ValueObjectId::class);
|
|
});
|
|
|
|
describe('class modifiers and immutability', function () use ($libraryNamespace): void {
|
|
arch('Api DTOs are final and readonly')
|
|
->expect("{$libraryNamespace}\Lib\ApiDto\Dto")
|
|
->classes()
|
|
->toBeReadonly()
|
|
->toBeFinal()
|
|
->ignoring([
|
|
Dto::class,
|
|
AbstractIncoCompoPartDto::class,
|
|
IncoCompoPartDto::class,
|
|
PortionType::class,
|
|
]);
|
|
|
|
arch('Value objects are final and readonly')
|
|
->expect("{$libraryNamespace}\Lib\ApiValueObject")
|
|
->classes()
|
|
->toBeReadonly()
|
|
->toBeFinal();
|
|
|
|
arch('Auth value objects are final and readonly')
|
|
->expect([
|
|
ClientIdentifier::class,
|
|
ClientSecret::class,
|
|
Nonce::class,
|
|
])
|
|
->classes()
|
|
->toBeReadonly()
|
|
->toBeFinal();
|
|
|
|
arch('Concrete errors are final and readonly')
|
|
->expect("{$libraryNamespace}\Error")
|
|
->classes()
|
|
->toBeReadonly()
|
|
->toBeFinal()
|
|
->ignoring([
|
|
ApiError::class,
|
|
AbstractApiError::class,
|
|
]);
|
|
|
|
arch('Factories are final')
|
|
->expect("{$libraryNamespace}\Factory")
|
|
->classes()
|
|
->toBeFinal();
|
|
|
|
arch('Concrete services are final')
|
|
->expect("{$libraryNamespace}\Service")
|
|
->classes()
|
|
->toBeFinal();
|
|
|
|
arch('Concrete repositories are final')
|
|
->expect("{$libraryNamespace}\Repository")
|
|
->classes()
|
|
->toBeFinal()
|
|
->ignoring([AbstractApiRepository::class]);
|
|
|
|
arch('Middlewares are final')
|
|
->expect([
|
|
ApiMandatoryHeadersMiddleware::class,
|
|
ApiLoggerMiddleware::class,
|
|
])
|
|
->classes()
|
|
->toBeFinal();
|
|
|
|
arch('Pagination Value objects are final and readonly')
|
|
->expect([
|
|
PageNumber::class,
|
|
PageSize::class,
|
|
])
|
|
->classes()
|
|
->toBeReadonly()
|
|
->toBeFinal();
|
|
|
|
arch('Pagination is final')
|
|
->expect(Pagination::class)
|
|
->toBeFinal();
|
|
});
|
|
|
|
describe('layer isolation', function () use ($libraryNamespace): void {
|
|
arch('DTOs do not depend on Repositories, Services, or Mappers')
|
|
->expect("{$libraryNamespace}\Lib\ApiDto")
|
|
->not->toUse([
|
|
"{$libraryNamespace}\Repository",
|
|
"{$libraryNamespace}\Service",
|
|
"{$libraryNamespace}\Mapper",
|
|
]);
|
|
|
|
arch('Value objects do not depend on Repositories, Services, Mappers, or DTOs')
|
|
->expect("{$libraryNamespace}\Lib\ApiValueObject")
|
|
->not->toUse([
|
|
"{$libraryNamespace}\Repository",
|
|
"{$libraryNamespace}\Service",
|
|
"{$libraryNamespace}\Mapper",
|
|
"{$libraryNamespace}\Lib\ApiDto",
|
|
]);
|
|
|
|
arch('Errors do not depend on Repositories, Services, Mappers, or DTOs')
|
|
->expect("{$libraryNamespace}\Error")
|
|
->not->toUse([
|
|
"{$libraryNamespace}\Repository",
|
|
"{$libraryNamespace}\Service",
|
|
"{$libraryNamespace}\Mapper",
|
|
"{$libraryNamespace}\Lib\ApiDto",
|
|
]);
|
|
|
|
arch('Repositories do not depend on Services')
|
|
->expect("{$libraryNamespace}\Repository")
|
|
->not->toUse([
|
|
"{$libraryNamespace}\Service",
|
|
]);
|
|
|
|
arch('Mappers do not depend on Repositories or Services')
|
|
->expect("{$libraryNamespace}\Mapper")
|
|
->not->toUse([
|
|
"{$libraryNamespace}\Repository",
|
|
"{$libraryNamespace}\Service",
|
|
]);
|
|
});
|