toBe('pgsql_supplier'); }); test('failed_webhook_jobs INSERT с tenant_id=NULL проходит под pgsql_supplier (BLOCKER #6)', function (): void { // Под обычной ролью policy tenant_isolation USING (tenant_id = current_setting('app.current_tenant_id')::bigint) // отвергает NULL (NULL :: bigint = NULL, NULL = '0'::bigint → NULL → false). // Под pgsql_supplier (BYPASSRLS на prod / postgres superuser на dev) INSERT проходит. DB::connection('pgsql_supplier')->table('failed_webhook_jobs')->insert([ 'tenant_id' => null, 'webhook_log_id' => null, 'raw_payload' => json_encode(['supplier_lead_id' => 42, 'project' => 'B1_test.ru']), 'exception' => 'simulated failure for BLOCKER #6 regression test', 'retry_count' => 3, 'failed_at' => now(), ]); $exists = DB::connection('pgsql_supplier') ->table('failed_webhook_jobs') ->whereNull('tenant_id') ->where('exception', 'simulated failure for BLOCKER #6 regression test') ->exists(); expect($exists)->toBeTrue(); }); test("LeadRouter видит проекты всех tenant'ов под pgsql_supplier без SET LOCAL (WARN #2)", function (): void { // 3 tenant × 2 проекта = 6 проектов, все привязаны к одному supplier_project. // БЕЗ SET LOCAL app.current_tenant_id (он уже '0' из beforeEach) — под обычной // ролью RLS отбросил бы всё; под pgsql_supplier (BYPASSRLS) видны все 6. $supplier = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'plan3-task3-warn2.example.com', ]); $tenants = Tenant::factory()->count(3)->create(['balance_leads' => 100]); foreach ($tenants as $tenant) { for ($i = 0; $i < 2; $i++) { Project::factory()->create([ 'tenant_id' => $tenant->id, 'supplier_b1_project_id' => $supplier->id, 'signal_type' => 'site', 'signal_identifier' => 'plan3-task3-warn2.example.com', 'is_active' => true, 'daily_limit_target' => 10, 'delivered_today' => 0, 'delivery_days_mask' => 127, 'region_mask' => 255, 'region_mode' => 'include', ]); } } $router = app(LeadRouter::class); $eligible = $router->matchEligibleProjects($supplier, '79991234567'); expect($eligible)->toHaveCount(6); }); test("ResetDeliveredTodayCommand сбрасывает delivered_today по всем tenant'ам (WARN #3)", function (): void { // Создаём 3 tenant'а с проектами, у каждого delivered_today=5. // Команда должна сбросить все 3 → 0 (под pgsql_supplier BYPASSRLS — без SET LOCAL). $tenants = Tenant::factory()->count(3)->create(); $projectIds = []; foreach ($tenants as $tenant) { $project = Project::factory()->create([ 'tenant_id' => $tenant->id, 'delivered_today' => 5, ]); $projectIds[] = $project->id; } // @phpstan-ignore-next-line method.notFound (Pest TestCall->artisan() mixin) $this->artisan('projects:reset-delivered-today')->assertExitCode(0); $remaining = DB::connection('pgsql_supplier') ->table('projects') ->whereIn('id', $projectIds) ->where('delivered_today', '>', 0) ->count(); expect($remaining)->toBe(0); });