[DatabaseTenancyBootstrapper::class], ]); DatabaseTenancyBootstrapper::$harden = $harden; Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) { return $event->tenant; })->toListener()); $tenant = Tenant::create(); $tenant->update([ 'tenancy_db_name' => config('database.connections.central.database'), // Central database name ]); if ($harden) { // Harden blocks initialization for tenants that use central database expect(fn () => tenancy()->initialize($tenant))->toThrow(RuntimeException::class); // Connection should be reverted back to central expect(DB::connection()->getName())->toBe('central'); } else { expect(fn() => tenancy()->initialize($tenant))->not()->toThrow(Throwable::class); // Connection not reverted to central expect(DB::connection()->getName())->toBe('tenant'); } })->with([ 'hardening enabled' => true, 'hardening disabled' => false, ]); test('harden prevents tenants from using a database of another tenant', function (bool $harden, string $connection) { config([ 'tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class], ]); DatabaseTenancyBootstrapper::$harden = $harden; Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) { return $event->tenant; })->toListener()); $tenant = Tenant::create(['tenancy_db_connection' => $connection]); $dbName = Str::random(8) . ($connection === 'sqlite' ? '.sqlite' : ''); Tenant::create(['tenancy_db_name' => $dbName, 'tenancy_db_connection' => $connection]); $tenant->update(['tenancy_db_name' => $dbName]); if ($harden) { // Harden blocks initialization for tenants that use a database of another tenant expect(fn () => tenancy()->initialize($tenant))->toThrow(RuntimeException::class); // Connection should be reverted back to central expect(DB::connection()->getName())->toBe('central'); } else { expect(fn() => tenancy()->initialize($tenant))->not()->toThrow(Throwable::class); // Connection not reverted to central expect(DB::connection()->getName())->toBe('tenant'); } })->with([ 'hardening enabled' => true, 'hardening disabled' => false, ])->with([ 'mysql' => 'mysql', 'named sqlite' => 'sqlite', ]); test('database tenancy bootstrapper throws an exception if DATABASE_URL is set', function (string|null $databaseUrl) { config(['database.connections.central.url' => $databaseUrl]); config(['tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class]]); Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) { return $event->tenant; })->toListener()); if ($databaseUrl) { expect(fn() => Tenant::create())->toThrow(QueryException::class); } else { expect(function() { $tenant1 = Tenant::create(); pest()->artisan('tenants:migrate'); tenancy()->initialize($tenant1); })->not()->toThrow(Throwable::class); } })->with(['abc.us-east-1.rds.amazonaws.com', null]);