40 lines
3.3 KiB
PHP
40 lines
3.3 KiB
PHP
<?php
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
require __DIR__.'/../vendor/autoload.php';
|
|
$app = require __DIR__.'/../bootstrap/app.php';
|
|
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
|
\App\Shared\Testing\TestingDatabaseGuard::assertSafe();
|
|
$fixture = json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'), true, 32, JSON_THROW_ON_ERROR);
|
|
$base = ['actor' => $fixture['manager_id'], 'tenant' => $fixture['tenant'], 'company' => $fixture['company'], 'unit' => $fixture['unit']];
|
|
function pair(array $firstInput, array $secondInput): array {
|
|
$first = new Process([PHP_BINARY, 'scripts/a-concurrency-worker.php', '--env=testing'], base_path(), null, json_encode($firstInput + ['hold' => true]));
|
|
$second = new Process([PHP_BINARY, 'scripts/a-concurrency-worker.php', '--env=testing'], base_path(), null, json_encode($secondInput));
|
|
$first->start();
|
|
if (! $first->waitUntil(fn ($type, $buffer) => str_contains($buffer, 'LOCK_ACQUIRED'))) { throw new RuntimeException('Could not establish concurrent lock test.'); }
|
|
$second->start(); $first->wait(); $second->wait();
|
|
$results = [];
|
|
foreach ([$first, $second] as $process) {
|
|
if (! $process->isSuccessful()) { throw new RuntimeException('Synthetic concurrency process failed.'); }
|
|
$lines = array_values(array_filter(explode("\n", trim($process->getOutput())), fn ($line) => str_starts_with($line, '{')));
|
|
$results[] = json_decode(end($lines), true, 32, JSON_THROW_ON_ERROR);
|
|
}
|
|
return $results;
|
|
}
|
|
$beforeEvents = DB::table('a_events')->count();
|
|
$key = (string) \Illuminate\Support\Str::uuid();
|
|
$input = $base + ['data' => ['name' => 'Cliente sintético de concorrência'], 'key' => $key];
|
|
[$first, $second] = pair($input, $input);
|
|
if ($first['status'] !== 200 || $second['status'] !== 200 || $first['result']['id'] !== $second['result']['id'] || ! $second['result']['replayed'] || DB::table('a_events')->count() !== $beforeEvents + 1) { throw new RuntimeException('Concurrent repetition was not deduplicated.'); }
|
|
$id = $first['result']['id'];
|
|
[$edit, $conflict] = pair($base + ['id' => $id, 'data' => ['name' => 'Versão concorrente preservada', 'version' => 1], 'key' => (string) \Illuminate\Support\Str::uuid()],
|
|
$base + ['id' => $id, 'data' => ['name' => 'Escrita antiga recusada', 'version' => 1], 'key' => (string) \Illuminate\Support\Str::uuid()]);
|
|
if ($edit['status'] !== 200 || $conflict['status'] !== 409 || DB::table('a_events')->count() !== $beforeEvents + 2 || DB::table('a_clients')->where('id', $id)->value('name') !== 'Versão concorrente preservada') { throw new RuntimeException('Concurrent version conflict was not protected.'); }
|
|
$report = ['environment' => 'sgf_tempo_test only', 'parallel_php_processes' => true, 'company_lock_overlap' => true,
|
|
'identical_create' => ['same_record' => true, 'second_replayed' => true, 'events_created' => 1],
|
|
'concurrent_edit' => ['first_status' => 200, 'second_status' => 409, 'version_after' => 2, 'events_created' => 1]];
|
|
file_put_contents(base_path('docs/proofs/a-concurrency.json'), json_encode($report, JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo "Concurrent PHP/PostgreSQL proof passed: one repeated creation, one version-conflicting edit, no duplicated events.\n";
|