mini_api_architecture_test.php 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. <?php
  2. /** Architectural guardrails required by the project: HTTP -> services -> Eloquent models. */
  3. require dirname(__DIR__) . '/vendor/autoload.php';
  4. $checks = 0;
  5. $assert = function (bool $condition, string $label) use (&$checks): void {
  6. $checks++;
  7. if (!$condition) {
  8. throw new RuntimeException($label);
  9. }
  10. };
  11. $root = dirname(__DIR__) . '/app/miniapi';
  12. foreach (glob($root . '/controller/*.php') as $path) {
  13. $source = file_get_contents($path);
  14. $assert(!preg_match('/::(?:query|where|find|transaction)\s*\(|\\\\model\\\\|getConnection\s*\(/', $source), basename($path) . ': controller must not query models');
  15. $class = new ReflectionClass('app\\miniapi\\controller\\' . basename($path, '.php'));
  16. $assert(count($class->getAttributes(\support\annotation\route\DisableDefaultRoute::class)) === 1, basename($path) . ': explicit routes only');
  17. }
  18. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root)) as $file) {
  19. if ($file->getExtension() !== 'php') {
  20. continue;
  21. }
  22. $source = file_get_contents($file->getPathname());
  23. $assert(!preg_match('/\b(?:Db|DB|PDO)::|\bnew\s+\\\\?PDO\b|->(?:selectRaw|whereRaw|statement|unprepared)\s*\(/', $source), $file->getFilename() . ': data access must use models');
  24. if (str_contains($file->getPathname(), DIRECTORY_SEPARATOR . 'service' . DIRECTORY_SEPARATOR)) {
  25. $assert(!preg_match('/use\s+(?:support|Webman\\\\Http)\\\\(?:Request|Response);/', $source), $file->getFilename() . ': service must not depend on HTTP');
  26. }
  27. }
  28. foreach (['start', 'checkin', 'draft', 'complete', 'exception'] as $action) {
  29. $assert(\app\miniapi\service\driver\TaskActionFactory::make($action) instanceof \app\miniapi\service\driver\TaskAction, 'driver action implements command contract');
  30. }
  31. try {
  32. \app\miniapi\service\driver\TaskActionFactory::make('unsupported');
  33. throw new RuntimeException('Unknown driver action accepted');
  34. } catch (\app\miniapi\exception\ApiException $error) {
  35. $assert($error->getCode() === 40401, 'factory rejects unsupported action');
  36. }
  37. echo "PASS: $checks architecture checks.\n";