Files
alrahma_sunday_school_api/tests/Feature/Api/V1/FullSurface/ApiDeepResponseShapeAndEnvelopeContractTest.php
root 5e35fefd69
API CI/CD / Validate (composer + pint) (push) Successful in 2m47s
API CI/CD / Test (PHPUnit) (push) Failing after 3m8s
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
fix test errors
2026-06-26 15:37:03 -04:00

106 lines
4.0 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\FullSurface;
use Illuminate\Routing\Route as LaravelRoute;
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;
class ApiDeepResponseShapeAndEnvelopeContractTest extends FullSurfaceE2EContractCase
{
public function test_successful_json_api_responses_use_parseable_json_envelopes_across_major_domains(): void
{
$failures = [];
foreach ($this->representativeDomainRoutes() as $route) {
$method = $this->primaryMethod($route);
$uri = $route->uri();
$response = $this->probeRoute($route);
$status = $response->getStatusCode();
if ($status >= 500) {
$failures[] = "$method $uri crashed with $status: ".$response->getContent();
continue;
}
if ($status >= 200 && $status < 300 && $response->getContent() !== '' && ! $this->isJsonString($response->getContent())) {
$failures[] = "$method $uri returned non-json successful API response: ".$response->getContent();
}
}
$this->assertSame([], $failures, "Successful API responses should be machine-readable JSON:\n".implode("\n", $failures));
}
public function test_collection_endpoints_expose_list_like_payloads_or_explicit_empty_state(): void
{
$failures = [];
foreach ($this->collectionLikeRoutes() as $route) {
$method = $this->primaryMethod($route);
$uri = $route->uri();
if ($method !== 'GET') {
continue;
}
$response = $this->probeRoute($route);
$status = $response->getStatusCode();
if (! in_array($status, [200, 204, 302, 401, 403, 404, 422], true)) {
$failures[] = "$method $uri returned unexpected $status for collection contract: ".$response->getContent();
continue;
}
if ($status === 200 && $this->isJsonString($response->getContent())) {
$payload = $response->json();
$hasCollectionShape = is_array($payload)
&& (array_is_list($payload)
|| array_key_exists('data', $payload)
|| array_key_exists('items', $payload)
|| array_key_exists('results', $payload)
|| array_key_exists('rows', $payload)
|| array_key_exists('ok', $payload)
|| array_key_exists('status', $payload));
if (! $hasCollectionShape) {
$failures[] = "$method $uri returned JSON but no recognizable collection/envelope keys: ".$response->getContent();
}
}
}
$this->assertSame([], $failures, "Collection routes need a stable list/envelope shape:\n".implode("\n", $failures));
}
/** @return list<LaravelRoute> */
private function representativeDomainRoutes(): array
{
return array_slice($this->apiRoutesContainingAny([
'auth', 'administrator', 'teachers', 'parents', 'students', 'attendance', 'scores',
'finance', 'inventory', 'messages', 'support', 'settings', 'school-years', 'families',
]), 0, 120);
}
/** @return list<LaravelRoute> */
private function collectionLikeRoutes(): array
{
return array_values(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
if ($this->primaryMethod($route) !== 'GET') {
return false;
}
$uri = $route->uri();
if (str_contains($uri, 'documentation') || str_contains($uri, 'docs/') || str_contains($uri, 'confirm_authorized_user')) {
return false;
}
return ! preg_match('#/\{[^}]+\}$#', $uri)
&& ! str_contains($uri, 'download')
&& ! str_contains($uri, 'export')
&& ! str_contains($uri, 'print')
&& ! str_contains($uri, 'pdf');
}));
}
}