2026-05-11 01:26:24 +03:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
|
|
namespace Tests\Concerns;
|
|
|
|
|
|
|
2026-06-25 08:19:53 +03:00
|
|
|
|
use Illuminate\Database\Connection;
|
2026-05-11 01:26:24 +03:00
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Share PDO between pgsql и pgsql_supplier connections в тестах.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Зачем: Laravel DatabaseTransactions trait оборачивает каждый connection в
|
|
|
|
|
|
* свою транзакцию. Записи через default Project::factory() ($pgsql) не видны
|
|
|
|
|
|
* с pgsql_supplier connection до commit'а. Sharing PDO означает — обе
|
|
|
|
|
|
* connection'ы используют ту же PDO session → одну транзакцию.
|
|
|
|
|
|
*
|
|
|
|
|
|
* На production обе connection'ы реальные separate PDO, и видимость
|
|
|
|
|
|
* обеспечивается READ COMMITTED после commit'а webhook-handler'а перед
|
|
|
|
|
|
* dispatch'ом supplier-flow job'а. Этот trait — только для test-окружения.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Использование: `uses(SharesSupplierPdo::class)` в Pest test-файле, где
|
|
|
|
|
|
* нужна cross-connection visibility (например, tests/Feature/Supplier/*).
|
|
|
|
|
|
*/
|
|
|
|
|
|
trait SharesSupplierPdo
|
|
|
|
|
|
{
|
|
|
|
|
|
protected function setUpSharesSupplierPdo(): void
|
|
|
|
|
|
{
|
|
|
|
|
|
if (! config()->has('database.connections.pgsql_supplier')) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$defaultConnection = DB::connection('pgsql');
|
2026-06-25 07:39:51 +03:00
|
|
|
|
$supplierConnection = DB::connection('pgsql_supplier');
|
|
|
|
|
|
$supplierConnection->setPdo($defaultConnection->getPdo());
|
|
|
|
|
|
$supplierConnection->setReadPdo($defaultConnection->getReadPdo());
|
|
|
|
|
|
|
|
|
|
|
|
// Шарим не только PDO, но и уровень вложенности транзакции. DatabaseTransactions
|
|
|
|
|
|
// уже открыл транзакцию на pgsql (этот же PDO) к моменту setUp этого трейта.
|
|
|
|
|
|
// Без синхронизации supplier-connection считает transactions=0 и при
|
|
|
|
|
|
// DB::connection('pgsql_supplier')->transaction() зовёт PDO->beginTransaction()
|
|
|
|
|
|
// на уже активной транзакции → PDOException "There is already an active transaction"
|
|
|
|
|
|
// (например AdminIncidentsController::notifyRkn). С синхронизацией вложенный
|
|
|
|
|
|
// transaction() делает SAVEPOINT внутри внешней транзакции (Postgres поддерживает),
|
|
|
|
|
|
// запись видна последующим чтениям и откатывается общим rollback'ом в teardown.
|
|
|
|
|
|
$level = $defaultConnection->transactionLevel();
|
|
|
|
|
|
if ($level > 0) {
|
2026-06-25 08:19:53 +03:00
|
|
|
|
$prop = new \ReflectionProperty(Connection::class, 'transactions');
|
2026-06-25 07:39:51 +03:00
|
|
|
|
$prop->setAccessible(true);
|
|
|
|
|
|
$prop->setValue($supplierConnection, $level);
|
|
|
|
|
|
}
|
2026-05-11 01:26:24 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|