Files
alrahma_sunday_school_api/tests/Feature/Api/V1/FullSurface/ApiDeepResponseShapeAndEnvelopeContractTest.php
T
2026-06-08 23:45:55 -04:00

100 lines
3.8 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();
return ! preg_match('#/\{[^}]+\}$#', $uri)
&& ! str_contains($uri, 'download')
&& ! str_contains($uri, 'export')
&& ! str_contains($uri, 'print')
&& ! str_contains($uri, 'pdf');
}));
}
}