add test batches
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Api\V1\FullSurface;
|
||||
|
||||
use Illuminate\Routing\Route as LaravelRoute;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;
|
||||
|
||||
class ApiWriteAuditAndTimestampContractTest extends FullSurfaceE2EContractCase
|
||||
{
|
||||
public function test_successful_create_responses_expose_identifier_or_resource_envelope(): void
|
||||
{
|
||||
foreach ($this->createRoutes() as $route) {
|
||||
$method = $this->primaryMethod($route);
|
||||
$uri = $route->uri();
|
||||
$response = $this->requestAs($this->actorFor($uri), $method, $uri, $this->payloadFor($method, $uri));
|
||||
|
||||
$this->assertControlled($response, $method, $uri);
|
||||
$this->assertNoServerError($response, "$method $uri create identifier contract");
|
||||
|
||||
if (in_array($response->getStatusCode(), [200, 201, 202], true) && $this->isJsonString($response->getContent())) {
|
||||
$json = $response->json();
|
||||
$flat = json_encode($json, JSON_THROW_ON_ERROR);
|
||||
$this->assertTrue(
|
||||
str_contains($flat, 'id') || str_contains($flat, 'uuid') || str_contains($flat, 'ok') || str_contains($flat, 'status'),
|
||||
"$method $uri successful create response should expose an identifier, ok/status, or resource envelope."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_successful_write_responses_do_not_emit_broken_timestamp_fields(): void
|
||||
{
|
||||
foreach ($this->timestampAwareWriteRoutes() as $route) {
|
||||
$method = $this->primaryMethod($route);
|
||||
$uri = $route->uri();
|
||||
$response = $this->requestAs($this->actorFor($uri), $method, $uri, $this->payloadFor($method, $uri));
|
||||
|
||||
$this->assertControlled($response, $method, $uri);
|
||||
$this->assertNoServerError($response, "$method $uri timestamp serialization contract");
|
||||
|
||||
if (in_array($response->getStatusCode(), [200, 201, 202], true) && $this->isJsonString($response->getContent())) {
|
||||
$flat = json_encode($response->json(), JSON_THROW_ON_ERROR);
|
||||
|
||||
foreach (['created_at', 'updated_at', 'deleted_at'] as $field) {
|
||||
if (! str_contains($flat, $field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->assertDoesNotMatchRegularExpression(
|
||||
'/"' . $field . '"\s*:\s*"(?:0000-00-00|Invalid date|NaN)/i',
|
||||
$flat,
|
||||
"$method $uri emitted a broken $field timestamp."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_audit_like_routes_are_not_publicly_mutable(): void
|
||||
{
|
||||
foreach ($this->auditOrLogRoutes() as $route) {
|
||||
$method = $this->primaryMethod($route);
|
||||
$uri = $route->uri();
|
||||
$response = $this->json($method, $this->materializePath($uri), $this->payloadFor($method, $uri));
|
||||
|
||||
$this->assertStatusIn($response, [302, 401, 403, 404, 405, 419, 422], "$method $uri public audit mutation");
|
||||
$this->assertNoServerError($response, "$method $uri public audit mutation");
|
||||
}
|
||||
}
|
||||
|
||||
/** @return list<LaravelRoute> */
|
||||
private function createRoutes(): array
|
||||
{
|
||||
return array_slice(array_filter($this->apiRoutes(), fn (LaravelRoute $route): bool => $this->primaryMethod($route) === 'POST' && ! str_contains($route->uri(), 'login')), 0, 80);
|
||||
}
|
||||
|
||||
/** @return list<LaravelRoute> */
|
||||
private function timestampAwareWriteRoutes(): array
|
||||
{
|
||||
return array_slice(array_filter($this->apiRoutes(), fn (LaravelRoute $route): bool => in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH'], true)), 0, 50);
|
||||
}
|
||||
|
||||
/** @return list<LaravelRoute> */
|
||||
private function auditOrLogRoutes(): array
|
||||
{
|
||||
return array_slice(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
|
||||
return preg_match('/audit|log|history|activity|movement|transaction/i', $route->uri()) === 1
|
||||
&& in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH', 'DELETE'], true);
|
||||
}), 0, 40);
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function candidateTablesFor(string $uri): array
|
||||
{
|
||||
return array_values(array_filter([
|
||||
str_contains($uri, 'users') ? 'users' : null,
|
||||
str_contains($uri, 'students') ? 'students' : null,
|
||||
str_contains($uri, 'attendance') ? 'attendance' : null,
|
||||
str_contains($uri, 'invoice') ? 'invoices' : null,
|
||||
str_contains($uri, 'payment') ? 'payments' : null,
|
||||
str_contains($uri, 'inventory') ? 'inventory_items' : null,
|
||||
str_contains($uri, 'message') ? 'messages' : null,
|
||||
str_contains($uri, 'support') ? 'support_tickets' : null,
|
||||
]));
|
||||
}
|
||||
|
||||
private function tableHasColumn(string $table, string $column): bool
|
||||
{
|
||||
try {
|
||||
return DB::getSchemaBuilder()->hasColumn($table, $column);
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user