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.'"'); }