get('/_test/tenant-context', function () { // current_setting вернёт NULL если не установлено (с missing_ok=true). $value = DB::selectOne("SELECT current_setting('app.current_tenant_id', true) AS v")->v; return response()->json(['tenant_id' => $value]); }); }); test('middleware возвращает 403 без tenant context', function () { $response = $this->get('/_test/tenant-context'); $response->assertStatus(403); }); test('middleware устанавливает tenant_id из X-Tenant-Id header', function () { $tenant = Tenant::factory()->create(); $response = $this->withHeaders(['X-Tenant-Id' => (string) $tenant->id]) ->get('/_test/tenant-context'); $response->assertStatus(200); expect((int) $response->json('tenant_id'))->toBe($tenant->id); }); test('middleware игнорирует не-числовой X-Tenant-Id header', function () { $response = $this->withHeaders(['X-Tenant-Id' => 'not-a-number']) ->get('/_test/tenant-context'); $response->assertStatus(403); }); test('middleware резолвит tenant_id по subdomain', function () { $tenant = Tenant::factory()->create(['subdomain' => 'acme-corp']); $response = $this->get('http://acme-corp.liderra.ru/_test/tenant-context'); $response->assertStatus(200); expect((int) $response->json('tenant_id'))->toBe($tenant->id); }); test('middleware с tenant_id корректно фильтрует данные через RLS', function () { // Этот тест НЕ работает на postgres-superuser (BYPASSRLS), // но показывает что middleware устанавливает контекст для будущих // запросов под crm_app_user. Проверка эквивалентности значения. $tenant1 = Tenant::factory()->create(); $tenant2 = Tenant::factory()->create(); Project::factory()->count(2)->create(['tenant_id' => $tenant1->id]); Project::factory()->count(3)->create(['tenant_id' => $tenant2->id]); Route::middleware([SetTenantContext::class])->get('/_test/projects-count', function () { // Без BYPASSRLS было бы COUNT только tenant1's projects $tenant1Count = DB::table('projects')->where('tenant_id', request()->header('X-Expected-Tenant'))->count(); return response()->json(['count' => $tenant1Count]); }); $response = $this->withHeaders([ 'X-Tenant-Id' => (string) $tenant1->id, 'X-Expected-Tenant' => (string) $tenant1->id, ])->get('/_test/projects-count'); $response->assertStatus(200); expect($response->json('count'))->toBe(2); });