2ad6e9cf02
API CI/CD / Validate (composer + pint) (push) Successful in 2m51s
API CI/CD / Test (PHPUnit) (push) Failing after 3m6s
API CI/CD / Build frontend assets (push) Failing after 5m23s
API CI/CD / Security audit (push) Failing after 34s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
50 lines
2.2 KiB
PHP
50 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\V1\FullSurface;
|
|
|
|
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;
|
|
|
|
class ApiConcurrencyAndStaleWriteContractTest extends FullSurfaceE2EContractCase
|
|
{
|
|
public function test_duplicate_update_payloads_remain_controlled_and_do_not_throw_server_errors(): void
|
|
{
|
|
foreach ($this->apiRoutesContainingAny(['profile', 'settings', 'attendance', 'inventory', 'payments', 'refunds', 'school-years', 'messages']) as $route) {
|
|
$method = $this->primaryMethod($route);
|
|
|
|
if (! in_array($method, ['PUT', 'PATCH', 'POST'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$payload = $this->payloadFor($method, $route->uri()) + [
|
|
'client_request_id' => 'e2e-concurrency-'.md5($route->uri()),
|
|
'updated_at' => '2000-01-01T00:00:00Z',
|
|
];
|
|
|
|
$first = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $payload);
|
|
$second = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $payload);
|
|
|
|
$this->assertControlled($first, $method, $route->uri());
|
|
$this->assertControlled($second, $method, $route->uri());
|
|
$this->assertStringNotContainsString('deadlock', strtolower($second->getContent()));
|
|
$this->assertStringNotContainsString('duplicate entry', strtolower($second->getContent()));
|
|
}
|
|
}
|
|
|
|
public function test_stale_if_match_headers_do_not_force_successful_mutations(): void
|
|
{
|
|
foreach ($this->apiRoutesContainingAny(['students', 'parents', 'inventory', 'payments', 'attendance', 'settings']) as $route) {
|
|
$method = $this->primaryMethod($route);
|
|
|
|
if (! in_array($method, ['PUT', 'PATCH', 'DELETE'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$response = $this->actingAs($this->actorFor($route->uri()), 'api')
|
|
->withHeader('If-Match', '"stale-version-that-should-not-win"')
|
|
->json($method, $this->materializePath($route->uri()), $this->payloadFor($method, $route->uri()));
|
|
|
|
$this->assertStatusIn($response, [200, 202, 204, 400, 401, 403, 404, 409, 412, 419, 422], "{$method} {$route->uri()} stale write");
|
|
}
|
|
}
|
|
}
|