41 lines
3.4 KiB
PHP
41 lines
3.4 KiB
PHP
<?php
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
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 = ['operation' => 'b1.confirm', 'actor' => $fixture['manager_id'], 'tenant' => $fixture['tenant'], 'company' => $fixture['company'], 'unit' => $fixture['unit'], 'version' => 1];
|
|
app(\App\Shared\Context\CompanyContext::class)->selectFor($base['actor'], $base['tenant'], $base['company'], $base['unit']);
|
|
$workflow = app(\App\Modules\Services\ServiceWorkflow::class);
|
|
$save = fn ($area, $data) => $workflow->save($area, null, $base['actor'], $data, (string) Str::uuid())['id'];
|
|
$client = $save('clients', ['name' => 'Cliente concorrência B1 sintético']);
|
|
$service = $save('services', ['name' => 'Serviço concorrência B1 sintético']);
|
|
$source = $save('service_drafts', ['client_id' => $client, 'lines' => [['service_id' => $service, 'quantity' => '1', 'unit_price' => '12.50']]]);
|
|
$reports = [];
|
|
foreach (['same_key', 'different_keys'] as $mode) {
|
|
$draft = $save('billing_drafts', ['source_id' => $source]);
|
|
$key = (string) Str::uuid();
|
|
$input = $base + ['id' => $draft, 'key' => $key];
|
|
$first = new Process([PHP_BINARY, 'scripts/a-concurrency-worker.php', '--env=testing'], base_path(), null, json_encode($input + ['hold' => true]));
|
|
$secondInput = $input; if ($mode === 'different_keys') { $secondInput['key'] = (string) Str::uuid(); }
|
|
$second = new Process([PHP_BINARY, 'scripts/a-concurrency-worker.php', '--env=testing'], base_path(), null, json_encode($secondInput));
|
|
$before = DB::table('a_events')->where('type', 'b1.billing_draft.confirmed_internal')->count();
|
|
$first->start();
|
|
if (! $first->waitUntil(fn ($type, $buffer) => str_contains($buffer, 'LOCK_ACQUIRED'))) { throw new RuntimeException('Concurrent lock not established.'); }
|
|
$second->start(); $first->wait(); $second->wait(); $results = [];
|
|
foreach ([$first, $second] as $process) {
|
|
if (! $process->isSuccessful()) { throw new RuntimeException('Synthetic worker 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);
|
|
}
|
|
if ($results[0]['status'] !== 200 || $results[1]['status'] !== 200 || $results[0]['result']['id'] !== $results[1]['result']['id'] || ! $results[1]['result']['replayed'] || DB::table('a_events')->where('type', 'b1.billing_draft.confirmed_internal')->count() !== $before + 1 || DB::table('b_internal_confirmations')->where('billing_draft_id', $draft)->count() !== 1) { throw new RuntimeException('Confirmation deduplication failed.'); }
|
|
$reports[$mode] = ['parallel_processes' => true, 'company_lock_overlap' => true, 'same_confirmation' => true, 'new_confirmations' => 1, 'new_events' => 1, 'second_replayed' => true];
|
|
}
|
|
file_put_contents(base_path('docs/proofs/b1-concurrency.json'), json_encode(['database' => 'sgf_tempo_test', 'results' => $reports], JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo "B1 concurrent confirmations passed with identical and different keys: one confirmation/event per draft.\n";
|