Files
portal/app/tests/TestCase.php
T
Дмитрий 6d6181b8cc feat(supplier): Plan 3 Task 3 — switch supplier-flow на pgsql_supplier (BYPASSRLS)
Закрывает 3 backlog-айтема Plan 2.6 одной правкой:
- BLOCKER #6: failed_webhook_jobs INSERT с tenant_id=NULL теперь проходит
  (BYPASSRLS обходит RLS-политику отвергавшую NULL под обычной ролью)
- WARN #2: LeadRouter::matchEligibleProjects видит projects всех tenant'ов
  через Project::on('pgsql_supplier') без SET LOCAL app.current_tenant_id
- WARN #3: ResetDeliveredTodayCommand обновляет projects всех tenant'ов
  через DB::connection('pgsql_supplier')

Архитектура: crm_supplier_worker BYPASSRLS-роль (создана Plan 2.6 #iv 7899071)
+ новый pgsql_supplier connection в config/database.php. WHERE(tenant_id=)
фильтры сохраняются как defense-in-depth.

Уточнение по Job's $connection: оригинальный план предполагал public $connection
= 'pgsql_supplier' на RouteSupplierLeadJob, но в Laravel Job's $connection
управляет очередью (sync/database/redis), не БД. Заменено на константу
RouteSupplierLeadJob::DB_CONNECTION + явный DB::connection(self::DB_CONNECTION)
в failed() callback'е. Это:
1) не ломает queue resolution (без этой правки тесты падают
   'pgsql_supplier queue connection has not been configured')
2) явно документирует intent — failed_webhook_jobs INSERT идёт через BYPASSRLS
3) handle()'s tenant-scoped транзакции остаются на default pgsql + SET LOCAL,
   где RLS нужна для defense-in-depth.

Также добавлено в tests/TestCase.php разделение PDO между pgsql и
pgsql_supplier connection'ами через setPdo/setReadPdo — иначе DatabaseTransactions
не откатывал бы supplier-side данные (две PDO-сессии = две независимые транзакции,
supplier не видит uncommitted INSERTs default-side).

Brainstorm decision: вариант C из 3 опций (A=schema bump, B=отдельная таблица,
C=BYPASSRLS-role). См. docs/superpowers/specs/2026-05-11-plan3-supplier-sync-design.md §1.

+4 теста в Feature/Supplier/SupplierConnectionTest.php (DB_CONNECTION constant +
BLOCKER#6 + WARN#2 + WARN#3). 0 schema changes.

Pest: 562/560 + 2 skipped (baseline 558/556 + 4 new = 562/560, ok). PHPStan: 0 errors
(добавлен 1 baseline entry для известного Pest+PHPStan limitation на artisan()).
Pint: clean.
2026-05-11 01:00:47 +03:00

35 lines
1.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\DB;
abstract class TestCase extends BaseTestCase
{
/**
* Plan 3 Task 3: share PDO between `pgsql` and `pgsql_supplier` connections в тестах,
* чтобы DatabaseTransactions corretly rollback'ил данные, созданные через дефолтный
* connection, но запрошенные через supplier. Без этого Project::on('pgsql_supplier')
* не видит свежесозданные через Project::factory() записи — две PDO-сессии = две
* разные транзакции, supplier-side не видит uncommitted INSERTs default-side.
*
* На production обе роли (crm_app_user + crm_supplier_worker) — две настоящие
* сессии, общая видимость через commit. В тестах достаточно одной разделяемой PDO.
*/
protected function setUp(): void
{
parent::setUp();
// После того как Laravel инициализировал dafault connection (pgsql),
// ре-используем его PDO для pgsql_supplier. Делаем только если оба
// connection'а есть в конфиге (продакшен может не иметь supplier).
if (config()->has('database.connections.pgsql_supplier')) {
$defaultPdo = DB::connection('pgsql')->getPdo();
$defaultReadPdo = DB::connection('pgsql')->getReadPdo();
DB::connection('pgsql_supplier')->setPdo($defaultPdo);
DB::connection('pgsql_supplier')->setReadPdo($defaultReadPdo);
}
}
}