85 lines
5.4 KiB
PHP
85 lines
5.4 KiB
PHP
<?php
|
|
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
require dirname(__DIR__).'/vendor/autoload.php';
|
|
$app = require dirname(__DIR__).'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
$config = DB::connection('pgsql')->getConfig();
|
|
unset($config['name']);
|
|
if ($config['host'] !== '127.0.0.1') {
|
|
throw new LogicException('Prova de contexto apenas em servidor local.');
|
|
}
|
|
$database = 'compras_receipts_test_'.bin2hex(random_bytes(6));
|
|
config(['database.connections.context_proof_admin' => [...$config, 'database' => 'postgres', 'url' => null]]);
|
|
$admin = DB::connection('context_proof_admin');
|
|
$admin->statement('CREATE DATABASE "'.$database.'"');
|
|
$workflow = in_array('--workflow', $argv ?? [], true);
|
|
$warehouse = in_array('--warehouse', $argv ?? [], true);
|
|
try {
|
|
$environment = ['APP_ENV' => 'testing', 'DB_CONNECTION' => 'pgsql', 'DB_URL' => '', 'DB_HOST' => $config['host'], 'DB_PORT' => (string) $config['port'],
|
|
'DB_DATABASE' => $database, 'DB_USERNAME' => $config['username'], 'DB_PASSWORD' => $config['password']];
|
|
$tests = $workflow ? ['tests/Feature/InternalRequisitionCandidateTest.php', 'tests/Feature/WorkContextTest.php'] : ['tests/Feature/WorkContextTest.php'];
|
|
if ($warehouse) {
|
|
$tests = ['tests/Feature/WarehouseRequestsTest.php', 'tests/Feature/WarehouseAvailabilityTest.php', 'tests/Feature/WarehouseFulfilmentTest.php'];
|
|
}
|
|
$process = new Process([PHP_BINARY, 'vendor/bin/pest', '--compact', ...$tests], dirname(__DIR__), $environment, timeout: 120);
|
|
$process->run();
|
|
echo $process->getOutput();
|
|
if (! $process->isSuccessful()) {
|
|
throw new RuntimeException('Prova PostgreSQL de contexto não aprovada.');
|
|
}
|
|
$races = [];
|
|
if ($warehouse) {
|
|
config(['database.connections.warehouse_proof_read' => [...$config, 'database' => $database, 'url' => null]]);
|
|
$reader = DB::connection('warehouse_proof_read');
|
|
foreach (['same_operation', 'different_operations'] as $scenario) {
|
|
$fixture = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'prepare'], dirname(__DIR__), $environment, timeout: 20);
|
|
$fixture->mustRun();
|
|
$data = json_decode($fixture->getOutput(), true, 32, JSON_THROW_ON_ERROR);
|
|
$first = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'hold', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 20);
|
|
$first->start();
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && ! str_contains($first->getOutput(), 'locked') && microtime(true) < $deadline) {
|
|
usleep(20000);
|
|
}
|
|
if (! str_contains($first->getOutput(), 'locked')) {
|
|
throw new RuntimeException('Primeiro executor sem bloqueio demonstrado.');
|
|
}
|
|
if ($scenario === 'different_operations') {
|
|
$data['input']['operation_key'] = (string) Str::uuid();
|
|
}
|
|
$second = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'run', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 20);
|
|
$second->start();
|
|
$blocked = false;
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && microtime(true) < $deadline) {
|
|
$blocked = (bool) $reader->selectOne("select exists(select 1 from pg_stat_activity where datname=? and wait_event_type='Lock') as blocked", [$database])->blocked;
|
|
if ($blocked) {
|
|
break;
|
|
} usleep(20000);
|
|
}
|
|
$first->wait();
|
|
$second->wait();
|
|
$a = json_decode(trim(str_replace('locked', '', $first->getOutput())), true, 16, JSON_THROW_ON_ERROR);
|
|
$b = json_decode(trim($second->getOutput()), true, 16, JSON_THROW_ON_ERROR);
|
|
if (! $blocked || $a['result'] !== 'saved' || ($scenario === 'same_operation' ? ($b['result'] !== 'saved' || $a['event'] !== $b['event']) : $b['result'] !== 'rejected')) {
|
|
throw new RuntimeException('Disputa não preservou entrega única.');
|
|
}
|
|
if ((int) $reader->table('warehouse_request_events')->where('warehouse_request_id', $data['request'])->where('action', 'deliver')->count() !== 1
|
|
|| (int) $reader->table('stock_movements')->where('tenant_id', $data['tenant'])->where('stock_item_id', $data['stock'])->where('warehouse_id', $data['warehouse'])->sum('quantity_milli') !== 6000) {
|
|
throw new RuntimeException('Movimento ou entrega duplicados.');
|
|
}
|
|
$races[] = ['scenario' => $scenario, 'real_lock_contention' => true, 'result' => 'passed'];
|
|
}
|
|
DB::purge('warehouse_proof_read');
|
|
}
|
|
file_put_contents(dirname(__DIR__).'/docs/proofs/'.($warehouse ? 'warehouse-drafts-pg' : ($workflow ? 'workflow-notices-pg' : 'work-context-pg')).'.json', json_encode(['checked_at' => gmdate('c'), 'database' => 'disposable PostgreSQL', 'result' => trim($process->getOutput()), 'races' => $races, 'persistent_data_changed' => false, 'database_removed_in_finally' => true], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
|
} finally {
|
|
$admin->select('select pg_terminate_backend(pid) from pg_stat_activity where datname = ? and pid <> pg_backend_pid()', [$database]);
|
|
$admin->statement('DROP DATABASE "'.$database.'"');
|
|
}
|