$directories * @return array */ function getTestFiles(array $directories): array { $files = []; foreach ($directories as $dir) { $path = dirname(__DIR__, 2) . '/' . ltrim($dir, '/'); if (! is_dir($path)) { continue; } $dirIterator = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); $iterator = new \RecursiveIteratorIterator($dirIterator); /** @var \SplFileInfo $file */ foreach ($iterator as $file) { if ($file->isFile() && str_ends_with($file->getFilename(), 'Test.php')) { $files[] = $file->getPathname(); } } } return $files; } } describe('Coding Standards & Project Rules', function (): void { arch()->preset()->php(); arch()->preset()->security(); arch('assert is not used in src (use Webmozart\Assert instead)') ->expect('assert') ->not->toBeUsed(); arch('config() helper is not used in src (use Config facade instead)') ->expect('config') ->not->toBeUsed(); arch('env() helper is not used in src (use Config facade instead)') ->expect('env') ->not->toBeUsed(); arch('debug functions are not used in src') ->expect(['dd', 'dump', 'var_dump', 'die', 'print_r', 'ray']) ->not->toBeUsed(); arch('shell execution functions are not used in src') ->expect(['exec', 'shell_exec', 'system', 'passthru', 'proc_open']) ->not->toBeUsed(); arch('eval function is not used in src') ->expect('eval') ->not->toBeUsed(); arch('insecure hashing functions are not used in src') ->expect(['md5', 'sha1']) ->not->toBeUsed(); arch('sleep functions are not used in src') ->expect(['sleep', 'usleep']) ->not->toBeUsed(); arch('mutable DateTime is not used in src (use DateTimeImmutable or DateTimeInterface)') ->expect('DateTime') ->not->toBeUsed(); arch('all Domain source files use strict types') ->expect('Domain') ->toUseStrictTypes(); arch('all Application source files use strict types') ->expect('Application') ->toUseStrictTypes(); arch('all Presentation source files use strict types') ->expect('Presentation') ->toUseStrictTypes(); arch('all Infra source files use strict types') ->expect('Infra') ->toUseStrictTypes(); arch('all Shared source files use strict types') ->expect('Shared') ->toUseStrictTypes(); arch('all test files belong to Tests namespace and use strict types') ->expect('Tests') ->toUseStrictTypes(); it('ensures every database seeder has a corresponding integration test', function (): void { $seederFiles = glob(dirname(__DIR__, 2) . '/database/seeders/*Seeder.php') ?: []; foreach ($seederFiles as $file) { $basename = basename($file, '.php'); $testFile = dirname(__DIR__, 2) . "/tests/Integration/Database/Seeders/{$basename}Test.php"; expect(file_exists($testFile))->toBeTrue( "Missing integration test for seeder [{$basename}]. Expected at [{$testFile}]." ); } }); it('ensures every test file in tests directory has a namespace', function (): void { foreach (getTestFiles(['tests']) as $file) { $content = (string) file_get_contents($file); expect($content)->toMatch( '/^\s*namespace\s+Tests/m', "File [{$file}] is missing a namespace Tests declaration." ); } }); it('ensures native classes in global namespace are not imported with use statements in src', function (): void { $srcDir = dirname(__DIR__, 2) . '/src'; $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($srcDir, \RecursiveDirectoryIterator::SKIP_DOTS)); /** @var \SplFileInfo $file */ foreach ($iterator as $file) { if ($file->isFile() && $file->getExtension() === 'php') { $content = (string) file_get_contents($file->getPathname()); if (preg_match('/^use\s+([A-Z][a-zA-Z0-9_]*)\s*;/m', $content, $matches)) { $className = $matches[1]; if (class_exists('\\' . $className) || interface_exists('\\' . $className)) { expect(true)->toBeFalse( sprintf('File [%s] imports global native class [%s] with a use statement. Use [\\%s] directly instead.', $file->getPathname(), $className, $className) ); } } } } expect(true)->toBeTrue(); }); });