8661615717
API CI/CD / Validate (composer + pint) (push) Successful in 2m46s
API CI/CD / Test (PHPUnit) (push) Failing after 3m3s
API CI/CD / Build frontend assets (push) Failing after 5m22s
API CI/CD / Security audit (push) Failing after 34s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
62 lines
2.5 KiB
PHP
62 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\V1\FullSurface;
|
|
|
|
use Illuminate\Support\Facades\Route;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;
|
|
|
|
class ApiOpenApiDocumentationDriftContractTest extends FullSurfaceE2EContractCase
|
|
{
|
|
#[Test]
|
|
public function api_surface_has_documentation_entry_points_and_route_inventory_remains_machine_readable(): void
|
|
{
|
|
$documentationRoutes = $this->apiRoutesContainingAny(['docs', 'documentation', 'openapi', 'swagger']);
|
|
|
|
$this->assertNotEmpty($documentationRoutes, 'The API needs at least one documentation route. Tribal knowledge is not documentation, despite industry folklore.');
|
|
|
|
foreach ($documentationRoutes as $route) {
|
|
$response = $this->probeRoute($route);
|
|
$this->assertControlled($response, $this->primaryMethod($route), $route->uri());
|
|
}
|
|
|
|
$apiRoutes = $this->apiRoutes();
|
|
$this->assertGreaterThan(25, count($apiRoutes), 'The full-surface suite expects the application API route table to be loaded.');
|
|
|
|
foreach ($apiRoutes as $route) {
|
|
$this->assertNotEmpty($route->uri(), 'Every API route must have a URI. Revolutionary stuff.');
|
|
$this->assertNotEmpty($route->methods(), 'Every API route must expose at least one method.');
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function named_api_routes_use_stable_names_when_names_exist(): void
|
|
{
|
|
foreach ($this->apiRoutes() as $route) {
|
|
$name = $route->getName();
|
|
|
|
if ($name === null) {
|
|
continue;
|
|
}
|
|
|
|
$this->assertStringNotContainsString(' ', $name, "Route name [$name] should not contain spaces.");
|
|
$this->assertStringNotContainsString('tmp', strtolower($name), "Route name [$name] looks temporary.");
|
|
$this->assertStringNotContainsString('test', strtolower($name), "Route name [$name] looks like test scaffolding leaked into routing.");
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function documented_routes_do_not_point_to_missing_actions(): void
|
|
{
|
|
foreach (Route::getRoutes()->getRoutes() as $route) {
|
|
if (! str_starts_with($route->uri(), 'api/')) {
|
|
continue;
|
|
}
|
|
|
|
$action = $route->getActionName();
|
|
$this->assertNotSame('Closure', $action, 'API routes should not depend on anonymous closures because documentation and traceability suffer.');
|
|
$this->assertNotEmpty($action, 'API route action must be discoverable.');
|
|
}
|
|
}
|
|
}
|