1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 12:54:05 +00:00

Merge branch 'master' into shared-users

This commit is contained in:
Samuel Štancl 2020-05-13 00:33:27 +02:00 committed by GitHub
commit 8915297c30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 880 additions and 3280 deletions

View file

@ -154,6 +154,12 @@ class BootstrapperTest extends TestCase
FilesystemTenancyBootstrapper::class
]]);
$old_storage_path = storage_path();
$old_storage_facade_roots = [];
foreach (config('tenancy.filesystem.disks') as $disk) {
$old_storage_facade_roots[$disk] = config("filesystems.disks.{$disk}.root");
}
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
@ -176,6 +182,27 @@ class BootstrapperTest extends TestCase
tenancy()->initialize($tenant3);
$this->assertFalse(Storage::disk('public')->exists('foo'));
$this->assertFalse(Storage::disk('public')->exists('abc'));
// Check suffixing logic
$new_storage_path = storage_path();
$this->assertEquals($old_storage_path . '/' . config('tenancy.filesystem.suffix_base') . tenant('id'), $new_storage_path);
foreach (config('tenancy.filesystem.disks') as $disk) {
$suffix = config('tenancy.filesystem.suffix_base') . tenant('id');
$current_path_prefix = Storage::disk($disk)->getAdapter()->getPathPrefix();
if ($override = config("tenancy.filesystem.root_override.{$disk}")) {
$correct_path_prefix = str_replace('%storage_path%', storage_path(), $override);
} else {
if ($base = $old_storage_facade_roots[$disk]) {
$correct_path_prefix = $base . "/$suffix/";
} else {
$correct_path_prefix = "$suffix/";
}
}
$this->assertSame($correct_path_prefix, $current_path_prefix);
}
}
// for queues see QueueTest

View file

@ -2,25 +2,41 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Stancl\Tenancy\Tenant;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class CacheManagerTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
config(['tenancy.bootstrappers' => [
CacheTenancyBootstrapper::class,
]]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
}
/** @test */
public function default_tag_is_automatically_applied()
{
$this->createTenant();
$this->initTenancy();
tenancy()->initialize(Tenant::create());
$this->assertArrayIsSubset([config('tenancy.cache.tag_base') . tenant('id')], cache()->tags('foo')->getTags()->getNames());
}
/** @test */
public function tags_are_merged_when_array_is_passed()
{
$this->createTenant();
$this->initTenancy();
tenancy()->initialize(Tenant::create());
$expected = [config('tenancy.cache.tag_base') . tenant('id'), 'foo', 'bar'];
$this->assertEquals($expected, cache()->tags(['foo', 'bar'])->getTags()->getNames());
}
@ -28,8 +44,8 @@ class CacheManagerTest extends TestCase
/** @test */
public function tags_are_merged_when_string_is_passed()
{
$this->createTenant();
$this->initTenancy();
tenancy()->initialize(Tenant::create());
$expected = [config('tenancy.cache.tag_base') . tenant('id'), 'foo'];
$this->assertEquals($expected, cache()->tags('foo')->getTags()->getNames());
}
@ -37,8 +53,8 @@ class CacheManagerTest extends TestCase
/** @test */
public function exception_is_thrown_when_zero_arguments_are_passed_to_tags_method()
{
$this->createTenant();
$this->initTenancy();
tenancy()->initialize(Tenant::create());
$this->expectException(\Exception::class);
cache()->tags();
}
@ -46,8 +62,8 @@ class CacheManagerTest extends TestCase
/** @test */
public function exception_is_thrown_when_more_than_one_argument_is_passed_to_tags_method()
{
$this->createTenant();
$this->initTenancy();
tenancy()->initialize(Tenant::create());
$this->expectException(\Exception::class);
cache()->tags(1, 2);
}
@ -55,14 +71,14 @@ class CacheManagerTest extends TestCase
/** @test */
public function tags_separate_cache_well_enough()
{
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$tenant1 = Tenant::create();
tenancy()->initialize($tenant1);
cache()->put('foo', 'bar', 1);
$this->assertSame('bar', cache()->get('foo'));
Tenant::new()->withDomains(['bar.localhost'])->save();
tenancy()->init('bar.localhost');
$tenant2 = Tenant::create();
tenancy()->initialize($tenant2);
$this->assertNotSame('bar', cache()->get('foo'));
@ -73,14 +89,14 @@ class CacheManagerTest extends TestCase
/** @test */
public function invoking_the_cache_helper_works()
{
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$tenant1 = Tenant::create();
tenancy()->initialize($tenant1);
cache(['foo' => 'bar'], 1);
$this->assertSame('bar', cache('foo'));
Tenant::new()->withDomains(['bar.localhost'])->save();
tenancy()->init('bar.localhost');
$tenant2 = Tenant::create();
tenancy()->initialize($tenant2);
$this->assertNotSame('bar', cache('foo'));
@ -91,32 +107,32 @@ class CacheManagerTest extends TestCase
/** @test */
public function cache_is_persisted()
{
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$tenant1 = Tenant::create();
tenancy()->initialize($tenant1);
cache(['foo' => 'bar'], 10);
$this->assertSame('bar', cache('foo'));
tenancy()->endTenancy();
tenancy()->end();
tenancy()->init('foo.localhost');
tenancy()->initialize($tenant1);
$this->assertSame('bar', cache('foo'));
}
/** @test */
public function cache_is_persisted_when_reidentification_is_used()
{
Tenant::new()->withDomains(['foo.localhost'])->save();
Tenant::new()->withDomains(['bar.localhost'])->save();
tenancy()->init('foo.localhost');
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
tenancy()->initialize($tenant1);
cache(['foo' => 'bar'], 10);
$this->assertSame('bar', cache('foo'));
tenancy()->init('bar.localhost');
tenancy()->endTenancy();
tenancy()->initialize($tenant2);
tenancy()->end();
tenancy()->init('foo.localhost');
tenancy()->initialize($tenant1);
$this->assertSame('bar', cache('foo'));
}
}

View file

@ -1,164 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Cache;
use Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver;
use Stancl\Tenancy\Tenant;
class CachedResolverTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
if (config('tenancy.storage_driver') !== 'db') {
$this->markTestSkipped('This test is only relevant for the DB storage driver.');
}
config(['tenancy.storage_drivers.db.cache_store' => config('cache.default')]);
}
/** @test */
public function a_query_is_not_made_for_tenant_id_once_domain_is_cached()
{
$tenant = Tenant::new()
->withData(['foo' => 'bar'])
->withDomains(['foo.localhost'])
->save();
// query is made
$queried = tenancy()->findByDomain('foo.localhost');
$this->assertEquals($tenant->data, $queried->data);
$this->assertSame($tenant->domains, $queried->domains);
// cache is set
$this->assertEquals($tenant->id, Cache::get('_tenancy_domain_to_id:foo.localhost'));
$this->assertEquals($tenant->data, Cache::get('_tenancy_id_to_data:' . $tenant->id));
$this->assertSame($tenant->domains, Cache::get('_tenancy_id_to_domains:' . $tenant->id));
// query is not made
DatabaseStorageDriver::getCentralConnection()->enableQueryLog();
$cached = tenancy()->findByDomain('foo.localhost');
$this->assertEquals($tenant->data, $cached->data);
$this->assertSame($tenant->domains, $cached->domains);
$this->assertSame([], DatabaseStorageDriver::getCentralConnection()->getQueryLog());
}
/** @test */
public function a_query_is_not_made_for_tenant_once_id_is_cached()
{
$tenant = Tenant::new()
->withData(['foo' => 'bar'])
->withDomains(['foo.localhost'])
->save();
// query is made
$queried = tenancy()->find($tenant->id);
$this->assertEquals($tenant->data, $queried->data);
$this->assertSame($tenant->domains, $queried->domains);
// cache is set
$this->assertEquals($tenant->data, Cache::get('_tenancy_id_to_data:' . $tenant->id));
$this->assertSame($tenant->domains, Cache::get('_tenancy_id_to_domains:' . $tenant->id));
// query is not made
DatabaseStorageDriver::getCentralConnection()->enableQueryLog();
$cached = tenancy()->find($tenant->id);
$this->assertEquals($tenant->data, $cached->data);
$this->assertSame($tenant->domains, $cached->domains);
$this->assertSame([], DatabaseStorageDriver::getCentralConnection()->getQueryLog());
}
/** @test */
public function modifying_tenant_domains_invalidates_the_cached_domain_to_id_mapping()
{
$tenant = Tenant::new()
->withDomains(['foo.localhost', 'bar.localhost'])
->save();
// queried
$this->assertSame($tenant->id, tenancy()->findByDomain('foo.localhost')->id);
$this->assertSame($tenant->id, tenancy()->findByDomain('bar.localhost')->id);
// assert cache set
$this->assertSame($tenant->id, Cache::get('_tenancy_domain_to_id:foo.localhost'));
$this->assertSame($tenant->id, Cache::get('_tenancy_domain_to_id:bar.localhost'));
$tenant
->removeDomains(['foo.localhost', 'bar.localhost'])
->addDomains(['xyz.localhost'])
->save();
// assert neither domain is cached
$this->assertSame(null, Cache::get('_tenancy_domain_to_id:foo.localhost'));
$this->assertSame(null, Cache::get('_tenancy_domain_to_id:bar.localhost'));
$this->assertSame(null, Cache::get('_tenancy_domain_to_id:xyz.localhost'));
}
/** @test */
public function modifying_tenants_data_invalidates_tenant_data_cache()
{
$tenant = Tenant::new()->withData(['foo' => 'bar'])->save();
// cache record is set
$this->assertSame('bar', tenancy()->find($tenant->id)->get('foo'));
$this->assertSame('bar', Cache::get('_tenancy_id_to_data:' . $tenant->id)['foo']);
// cache record is invalidated
$tenant->set('foo', 'xyz');
$this->assertSame(null, Cache::get('_tenancy_id_to_data:' . $tenant->id));
// cache record is set
$this->assertSame('xyz', tenancy()->find($tenant->id)->get('foo'));
$this->assertSame('xyz', Cache::get('_tenancy_id_to_data:' . $tenant->id)['foo']);
// cache record is invalidated
$tenant->foo = 'abc';
$tenant->save();
$this->assertSame(null, Cache::get('_tenancy_id_to_data:' . $tenant->id));
}
/** @test */
public function modifying_tenants_domains_invalidates_tenant_domain_cache()
{
$tenant = Tenant::new()
->withData(['foo' => 'bar'])
->withDomains(['foo.localhost'])
->save();
// cache record is set
$this->assertSame(['foo.localhost'], tenancy()->find($tenant->id)->domains);
$this->assertSame(['foo.localhost'], Cache::get('_tenancy_id_to_domains:' . $tenant->id));
// cache record is invalidated
$tenant->addDomains(['bar.localhost'])->save();
$this->assertEquals(null, Cache::get('_tenancy_id_to_domains:' . $tenant->id));
$this->assertEquals(['foo.localhost', 'bar.localhost'], tenancy()->find($tenant->id)->domains);
}
/** @test */
public function deleting_a_tenant_invalidates_all_caches()
{
$tenant = Tenant::new()
->withData(['foo' => 'bar'])
->withDomains(['foo.localhost'])
->save();
tenancy()->findByDomain('foo.localhost');
$this->assertEquals($tenant->id, Cache::get('_tenancy_domain_to_id:foo.localhost'));
$this->assertEquals($tenant->data, Cache::get('_tenancy_id_to_data:' . $tenant->id));
$this->assertEquals(['foo.localhost'], Cache::get('_tenancy_id_to_domains:' . $tenant->id));
$tenant->delete();
$this->assertEquals(null, Cache::get('_tenancy_domain_to_id:foo.localhost'));
$this->assertEquals(null, Cache::get('_tenancy_id_to_data:' . $tenant->id));
$this->assertEquals(null, Cache::get('_tenancy_id_to_domains:' . $tenant->id));
}
}

View file

@ -2,18 +2,41 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline;
use Stancl\Tenancy\Events\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class CommandsTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
config(['tenancy.bootstrappers' => [
DatabaseTenancyBootstrapper::class
]]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
}
/** @test */
public function migrate_command_doesnt_change_the_db_connection()
@ -32,35 +55,42 @@ class CommandsTest extends TestCase
/** @test */
public function migrate_command_works_without_options()
{
$tenant = Tenant::create();
$this->assertFalse(Schema::hasTable('users'));
Artisan::call('tenants:migrate');
$this->assertFalse(Schema::hasTable('users'));
tenancy()->init('test.localhost');
tenancy()->initialize($tenant);
$this->assertTrue(Schema::hasTable('users'));
}
/** @test */
public function migrate_command_works_with_tenants_option()
{
$tenant = Tenant::new()->withDomains(['test2.localhost'])->save();
$tenant = Tenant::create();
Artisan::call('tenants:migrate', [
'--tenants' => [$tenant['id']],
]);
$this->assertFalse(Schema::hasTable('users'));
tenancy()->init('test.localhost');
tenancy()->initialize(Tenant::create());
$this->assertFalse(Schema::hasTable('users'));
tenancy()->init('test2.localhost');
tenancy()->initialize($tenant);
$this->assertTrue(Schema::hasTable('users'));
}
/** @test */
public function rollback_command_works()
{
$tenant = Tenant::create();
Artisan::call('tenants:migrate');
$this->assertFalse(Schema::hasTable('users'));
tenancy()->init('test.localhost');
tenancy()->initialize($tenant);
$this->assertTrue(Schema::hasTable('users'));
Artisan::call('tenants:rollback');
$this->assertFalse(Schema::hasTable('users'));
@ -93,7 +123,7 @@ class CommandsTest extends TestCase
/** @test */
public function database_connection_is_switched_to_default_when_tenancy_has_been_initialized()
{
tenancy()->init('test.localhost');
tenancy()->initialize(Tenant::create());
$this->database_connection_is_switched_to_default();
}
@ -101,7 +131,7 @@ class CommandsTest extends TestCase
/** @test */
public function run_commands_works()
{
$id = Tenant::new()->withDomains(['run.localhost'])->save()['id'];
$id = Tenant::create()->id;
Artisan::call('tenants:migrate', ['--tenants' => [$id]]);
@ -121,34 +151,25 @@ class CommandsTest extends TestCase
mkdir($dir, 0777, true);
}
if (app()->version()[0] === '6') {
file_put_contents(app_path('Http/Kernel.php'), file_get_contents(__DIR__ . '/Etc/defaultHttpKernelv6.stub'));
} else {
file_put_contents(app_path('Http/Kernel.php'), file_get_contents(__DIR__ . '/Etc/defaultHttpKernelv7.stub'));
}
$this->artisan('tenancy:install')
->expectsQuestion('Do you wish to publish the migrations that create these tables?', 'yes');
$this->artisan('tenancy:install');
$this->assertFileExists(base_path('routes/tenant.php'));
$this->assertFileExists(base_path('config/tenancy.php'));
$this->assertFileExists(database_path('migrations/2019_09_15_000010_create_tenants_table.php'));
$this->assertFileExists(database_path('migrations/2019_09_15_000020_create_domains_table.php'));
$this->assertDirectoryExists(database_path('migrations/tenant'));
if (app()->version()[0] === '6') {
$this->assertSame(file_get_contents(__DIR__ . '/Etc/modifiedHttpKernelv6.stub'), file_get_contents(app_path('Http/Kernel.php')));
} else {
$this->assertSame(file_get_contents(__DIR__ . '/Etc/modifiedHttpKernelv7.stub'), file_get_contents(app_path('Http/Kernel.php')));
}
}
/** @test */
public function migrate_fresh_command_works()
{
$tenant = Tenant::create();
$this->assertFalse(Schema::hasTable('users'));
Artisan::call('tenants:migrate-fresh');
$this->assertFalse(Schema::hasTable('users'));
tenancy()->init('test.localhost');
tenancy()->initialize($tenant);
$this->assertTrue(Schema::hasTable('users'));
$this->assertFalse(DB::table('users')->exists());
@ -159,17 +180,4 @@ class CommandsTest extends TestCase
Artisan::call('tenants:migrate-fresh');
$this->assertFalse(DB::table('users')->exists());
}
/** @test */
public function create_command_works()
{
Artisan::call('tenants:create -d aaa.localhost -d bbb.localhost plan=free email=foo@test.local');
$tenant = tenancy()->all()[1]; // a tenant is autocreated prior to this
$data = $tenant->data;
unset($data['id']);
unset($data['_tenancy_db_name']);
$this->assertSame(['plan' => 'free', 'email' => 'foo@test.local'], $data);
$this->assertSame(['aaa.localhost', 'bbb.localhost'], $tenant->domains);
}
}

View file

@ -1,154 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\Etc\User;
class DataSeparationTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
/** @test */
public function databases_are_separated()
{
$tenant1 = Tenant::create('tenant1.localhost');
$tenant2 = Tenant::create('tenant2.localhost');
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant2['id']],
]);
tenancy()->init('tenant1.localhost');
User::create([
'name' => 'foo',
'email' => 'foo@bar.com',
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
]);
$this->assertSame('foo', User::first()->name);
tenancy()->init('tenant2.localhost');
$this->assertSame(null, User::first());
User::create([
'name' => 'xyz',
'email' => 'xyz@bar.com',
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
]);
$this->assertSame('xyz', User::first()->name);
$this->assertSame('xyz@bar.com', User::first()->email);
tenancy()->init('tenant1.localhost');
$this->assertSame('foo', User::first()->name);
$this->assertSame('foo@bar.com', User::first()->email);
$tenant3 = Tenant::create('tenant3.localhost');
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant3['id']],
]);
tenancy()->init('tenant3.localhost');
$this->assertSame(null, User::first());
tenancy()->init('tenant1.localhost');
DB::table('users')->where('id', 1)->update(['name' => 'xxx']);
$this->assertSame('xxx', User::first()->name);
}
/** @test */
public function redis_is_separated()
{
if (! config('tenancy.redis.tenancy')) {
$this->markTestSkipped('Redis tenancy disabled.');
}
Tenant::create('tenant1.localhost');
Tenant::create('tenant2.localhost');
tenancy()->init('tenant1.localhost');
Redis::set('foo', 'bar');
$this->assertSame('bar', Redis::get('foo'));
tenancy()->init('tenant2.localhost');
$this->assertSame(null, Redis::get('foo'));
Redis::set('foo', 'xyz');
Redis::set('abc', 'def');
$this->assertSame('xyz', Redis::get('foo'));
$this->assertSame('def', Redis::get('abc'));
tenancy()->init('tenant1.localhost');
$this->assertSame('bar', Redis::get('foo'));
$this->assertSame(null, Redis::get('abc'));
Tenant::create('tenant3.localhost');
tenancy()->init('tenant3.localhost');
$this->assertSame(null, Redis::get('foo'));
$this->assertSame(null, Redis::get('abc'));
}
/** @test */
public function cache_is_separated()
{
Tenant::create('tenant1.localhost');
Tenant::create('tenant2.localhost');
tenancy()->init('tenant1.localhost');
Cache::put('foo', 'bar', 60);
$this->assertSame('bar', Cache::get('foo'));
tenancy()->init('tenant2.localhost');
$this->assertSame(null, Cache::get('foo'));
Cache::put('foo', 'xyz', 60);
Cache::put('abc', 'def', 60);
$this->assertSame('xyz', Cache::get('foo'));
$this->assertSame('def', Cache::get('abc'));
tenancy()->init('tenant1.localhost');
$this->assertSame('bar', Cache::get('foo'));
$this->assertSame(null, Cache::get('abc'));
Tenant::create('tenant3.localhost');
tenancy()->init('tenant3.localhost');
$this->assertSame(null, Cache::get('foo'));
$this->assertSame(null, Cache::get('abc'));
}
/** @test */
public function filesystem_is_separated()
{
Tenant::create('tenant1.localhost');
Tenant::create('tenant2.localhost');
tenancy()->init('tenant1.localhost');
Storage::disk('public')->put('foo', 'bar');
$this->assertSame('bar', Storage::disk('public')->get('foo'));
tenancy()->init('tenant2.localhost');
$this->assertFalse(Storage::disk('public')->exists('foo'));
Storage::disk('public')->put('foo', 'xyz');
Storage::disk('public')->put('abc', 'def');
$this->assertSame('xyz', Storage::disk('public')->get('foo'));
$this->assertSame('def', Storage::disk('public')->get('abc'));
tenancy()->init('tenant1.localhost');
$this->assertSame('bar', Storage::disk('public')->get('foo'));
$this->assertFalse(Storage::disk('public')->exists('abc'));
Tenant::create('tenant3.localhost');
tenancy()->init('tenant3.localhost');
$this->assertFalse(Storage::disk('public')->exists('foo'));
$this->assertFalse(Storage::disk('public')->exists('abc'));
}
}

View file

@ -1,73 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Tenant;
class DatabaseManagerTest extends TestCase
{
/** @test */
public function reconnect_method_works()
{
$old_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
$this->createTenant();
$this->initTenancy();
app(\Stancl\Tenancy\DatabaseManager::class)->reconnect();
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
$this->assertSame($old_connection_name, $new_connection_name);
$this->assertNotEquals('tenant', $new_connection_name);
}
/** @test */
public function db_name_is_prefixed_with_db_path_when_sqlite_is_used()
{
if (file_exists(database_path('foodb'))) {
unlink(database_path('foodb')); // cleanup
}
config(['database.connections.fooconn.driver' => 'sqlite']);
$tenant = Tenant::new()->withData([
'_tenancy_db_name' => 'foodb',
'_tenancy_db_connection' => 'fooconn',
])->save();
app(DatabaseManager::class)->createTenantConnection($tenant);
$this->assertSame(config('database.connections.tenant.database'), database_path('foodb'));
}
/** @test */
public function the_default_db_is_used_when_template_connection_is_null()
{
$this->assertSame('central', config('database.default'));
config([
'database.connections.central.foo' => 'bar',
'tenancy.database.template_connection' => null,
]);
$this->createTenant();
$this->initTenancy();
$this->assertSame('tenant', config('database.default'));
$this->assertSame('bar', config('database.connections.' . config('database.default') . '.foo'));
}
/** @test */
public function ending_tenancy_doesnt_purge_the_central_connection()
{
$this->markTestIncomplete('Seems like this only happens on MySQL?');
// regression test for https://github.com/stancl/tenancy/pull/189
// config(['tenancy.migrate_after_creation' => true]);
tenancy()->create(['foo.localhost']);
tenancy()->init('foo.localhost');
tenancy()->end();
$this->assertNotEmpty(tenancy()->all());
tenancy()->all()->each->delete();
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Database\Seeder;
use Illuminate\Foundation\Auth\User as Authenticable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Jobs\MigrateDatabase;
use Stancl\Tenancy\Jobs\SeedDatabase;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\Tests\TestCase;
class DatabasePreparationTest extends TestCase
{
/** @test */
public function database_can_be_created_after_tenant_creation()
{
config(['tenancy.template_tenant_connection' => 'mysql']);
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
$tenant = Tenant::create();
$this->assertTrue(app(MySQLDatabaseManager::class)->databaseExists($tenant->database()->getName()));
}
/** @test */
public function database_can_be_migrated_after_tenant_creation()
{
Event::listen(TenantCreated::class, JobPipeline::make([
CreateDatabase::class,
MigrateDatabase::class,
])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
$tenant = Tenant::create();
$tenant->run(function () {
$this->assertTrue(Schema::hasTable('users'));
});
}
/** @test */
public function database_can_be_seeded_after_tenant_creation()
{
config(['tenancy.seeder_parameters' => [
'--class' => TestSeeder::class,
]]);
Event::listen(TenantCreated::class, JobPipeline::make([
CreateDatabase::class,
MigrateDatabase::class,
SeedDatabase::class,
])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
$tenant = Tenant::create();
$tenant->run(function () {
$this->assertSame('Seeded User', User::first()->name);
});
}
/** @test */
public function custom_job_can_be_added_to_the_pipeline()
{
config(['tenancy.seeder_parameters' => [
'--class' => TestSeeder::class,
]]);
Event::listen(TenantCreated::class, JobPipeline::make([
CreateDatabase::class,
MigrateDatabase::class,
SeedDatabase::class,
CreateSuperuser::class,
])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
$tenant = Tenant::create();
$tenant->run(function () {
$this->assertSame('Foo', User::all()[1]->name);
});
}
}
class User extends Authenticable
{
protected $guarded = [];
}
class TestSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('users')->insert([
'name' => 'Seeded User',
'email' => 'seeded@user',
'password' => bcrypt('password'),
]);
}
}
class CreateSuperuser
{
protected $tenant;
public function __construct(Tenant $tenant)
{
$this->tenant = $tenant;
}
public function handle()
{
$this->tenant->run(function () {
User::create(['name' => 'Foo', 'email' => 'foo@bar.com', 'password' => 'secret']);
});
}
}

View file

@ -1,144 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Str;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\Etc\User;
class DatabaseSchemaManagerTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = false;
protected function getEnvironmentSetUp($app)
{
parent::getEnvironmentSetUp($app);
$app['config']->set([
'database.default' => 'pgsql',
'tenancy.storage_drivers.db.connection' => 'pgsql',
'database.connections.pgsql.database' => 'main',
'database.connections.pgsql.schema' => 'public',
'tenancy.database.template_connection' => null,
'tenancy.database.suffix' => '',
'tenancy.database_managers.pgsql' => \Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class,
]);
}
/** @test */
public function reconnect_method_works()
{
$old_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
tenancy()->init('test.localhost');
app(\Stancl\Tenancy\DatabaseManager::class)->reconnect();
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
$this->assertSame($old_connection_name, $new_connection_name);
}
/** @test */
public function the_default_db_is_used_when_template_connection_is_null()
{
$this->assertSame('pgsql', config('database.default'));
config([
'database.connections.pgsql.foo' => 'bar',
'tenancy.database.template_connection' => null,
]);
tenancy()->init('test.localhost');
$this->assertSame('tenant', config('database.default'));
$this->assertSame('bar', config('database.connections.tenant.foo'));
}
/** @test */
public function make_sure_using_schema_connection()
{
$tenant = tenancy()->create(['schema.localhost']);
tenancy()->init('schema.localhost');
$this->assertSame($tenant->database()->getName(), config('database.connections.' . config('database.default') . '.schema'));
}
/** @test */
public function databases_are_separated_using_schema_and_not_database()
{
tenancy()->create('foo.localhost');
tenancy()->init('foo.localhost');
$this->assertSame('tenant', config('database.default'));
$this->assertSame('main', config('database.connections.tenant.database'));
$schema1 = config('database.connections.' . config('database.default') . '.schema');
$database1 = config('database.connections.' . config('database.default') . '.database');
tenancy()->create('bar.localhost');
tenancy()->init('bar.localhost');
$this->assertSame('tenant', config('database.default'));
$this->assertSame('main', config('database.connections.tenant.database'));
$schema2 = config('database.connections.' . config('database.default') . '.schema');
$database2 = config('database.connections.' . config('database.default') . '.database');
$this->assertSame($database1, $database2);
$this->assertNotSame($schema1, $schema2);
}
/** @test */
public function schemas_are_separated()
{
// copied from DataSeparationTest
$tenant1 = Tenant::create('tenant1.localhost');
$tenant2 = Tenant::create('tenant2.localhost');
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant2['id']],
]);
tenancy()->init('tenant1.localhost');
User::create([
'name' => 'foo',
'email' => 'foo@bar.com',
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
]);
$this->assertSame('foo', User::first()->name);
tenancy()->init('tenant2.localhost');
$this->assertSame(null, User::first());
User::create([
'name' => 'xyz',
'email' => 'xyz@bar.com',
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
]);
$this->assertSame('xyz', User::first()->name);
$this->assertSame('xyz@bar.com', User::first()->email);
tenancy()->init('tenant1.localhost');
$this->assertSame('foo', User::first()->name);
$this->assertSame('foo@bar.com', User::first()->email);
$tenant3 = Tenant::create('tenant3.localhost');
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant3['id']],
]);
tenancy()->init('tenant3.localhost');
$this->assertSame(null, User::first());
tenancy()->init('tenant1.localhost');
\DB::table('users')->where('id', 1)->update(['name' => 'xxx']);
$this->assertSame('xxx', User::first()->name);
}
}

View file

@ -2,15 +2,20 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Exceptions\TenantDatabaseUserAlreadyExistsException;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Tests\TestCase;
class DatabaseUsersTest extends TestCase
{
@ -21,14 +26,18 @@ class DatabaseUsersTest extends TestCase
config([
'tenancy.database_managers.mysql' => PermissionControlledMySQLDatabaseManager::class,
'tenancy.database.suffix' => '',
'tenancy.database.template_connection' => 'mysql',
'tenancy.template_tenant_connection' => 'mysql',
]);
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
}
/** @test */
public function users_are_created_when_permission_controlled_mysql_manager_is_used()
{
$tenant = Tenant::new()->withData([
$tenant = new Tenant([
'id' => 'foo' . Str::random(10),
]);
$tenant->database()->makeCredentials();
@ -46,9 +55,9 @@ class DatabaseUsersTest extends TestCase
public function a_tenants_database_cannot_be_created_when_the_user_already_exists()
{
$username = 'foo' . Str::random(8);
$tenant = Tenant::new()->withData([
'_tenancy_db_username' => $username,
])->save();
$tenant = Tenant::create([
'tenancy_db_username' => $username,
]);
/** @var ManagesDatabaseUsers $manager */
$manager = $tenant->database()->manager();
@ -56,9 +65,9 @@ class DatabaseUsersTest extends TestCase
$this->assertTrue($manager->databaseExists($tenant->database()->getName()));
$this->expectException(TenantDatabaseUserAlreadyExistsException::class);
$tenant2 = Tenant::new()->withData([
'_tenancy_db_username' => $username,
])->save();
$tenant2 = Tenant::create([
'tenancy_db_username' => $username,
]);
/** @var ManagesDatabaseUsers $manager */
$manager = $tenant2->database()->manager();
@ -73,9 +82,9 @@ class DatabaseUsersTest extends TestCase
'ALTER', 'ALTER ROUTINE', 'CREATE',
];
$tenant = Tenant::new()->withData([
'_tenancy_db_username' => $user = 'user' . Str::random(8),
])->save();
$tenant = Tenant::create([
'tenancy_db_username' => $user = 'user' . Str::random(8),
]);
$query = DB::connection('mysql')->select("SHOW GRANTS FOR `{$tenant->database()->getUsername()}`@`{$tenant->database()->connection()['host']}`")[1];
$this->assertStringStartsWith('GRANT CREATE, ALTER, ALTER ROUTINE ON', $query->{"Grants for {$user}@mysql"}); // @mysql because that's the hostname within the docker network
@ -90,15 +99,15 @@ class DatabaseUsersTest extends TestCase
'tenancy.database.template_connection' => 'mysql',
]);
$tenant = Tenant::new()->withData([
$tenant = Tenant::create([
'id' => 'foo' . Str::random(10),
])->save();
]);
$this->assertTrue($tenant->database()->manager() instanceof MySQLDatabaseManager);
$tenant = Tenant::new()->withData([
$tenant = Tenant::create([
'id' => 'foo' . Str::random(10),
])->save();
]);
tenancy()->initialize($tenant); // check if everything works
tenancy()->end();

View file

@ -39,10 +39,6 @@ class HttpKernel extends Kernel
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'tenancy' => [
\Stancl\Tenancy\Middleware\InitializeTenancy::class,
],
'api' => [
'throttle:60,1',
'bindings',
@ -66,18 +62,5 @@ class HttpKernel extends Kernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'tenancy' => \Stancl\Tenancy\Middleware\InitializeTenancy::class,
];
/**
* The priority-sorted list of middleware.
*
* This forces non-global middleware to always be in the given order.
*
* @var array
*/
protected $middlewarePriority = [
\Stancl\Tenancy\Middleware\InitializeTenancy::class,
];
}

View file

@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Tenancy;
use Tenant;
class FacadeTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = true;
/** @test */
public function tenant_manager_can_be_accessed_using_the_Tenancy_facade()
{
$this->assertSame(tenancy()->getTenant(), Tenancy::getTenant());
}
/** @test */
public function tenant_storage_can_be_accessed_using_the_Tenant_facade()
{
tenant()->put('foo', 'bar');
Tenant::put('abc', 'xyz');
$this->assertSame('bar', Tenant::get('foo'));
$this->assertSame('xyz', Tenant::get('abc'));
}
/** @test */
public function tenant_can_be_created_using_the_Tenant_facade()
{
$this->assertSame('bar', Tenant::create(['foo.localhost'], ['foo' => 'bar'])->foo);
}
}

View file

@ -2,17 +2,15 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\Features;
use Route;
use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Features\CrossDomainRedirect;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Tests\TestCase;
class RedirectTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
/** @test */
public function tenant_redirect_macro_replaces_only_the_hostname()
{
@ -28,8 +26,8 @@ class RedirectTest extends TestCase
return redirect()->route('home')->domain('abcd');
});
Tenant::create('foo.localhost');
tenancy()->init('foo.localhost');
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->get('/redirect')
->assertRedirect('http://abcd/foobar');
@ -42,9 +40,7 @@ class RedirectTest extends TestCase
return 'Foo';
})->name('foo');
$this->assertSame('http://foo.localhost/abcdef/as/df', tenant_route('foo', ['a' => 'as', 'b' => 'df'], 'foo.localhost'));
$this->assertSame('http://foo.localhost/abcdef', tenant_route('foo', [], 'foo.localhost'));
$this->assertSame('http://' . request()->getHost() . '/abcdef/x/y', tenant_route('foo', ['a' => 'x', 'b' => 'y']));
$this->assertSame('http://foo.localhost/abcdef/as/df', tenant_route('foo.localhost', 'foo', ['a' => 'as', 'b' => 'df']));
$this->assertSame('http://foo.localhost/abcdef', tenant_route('foo.localhost', 'foo', []));
}
}

View file

@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Stancl\Tenancy\Contracts\Future\CanFindByAnyKey;
use Stancl\Tenancy\Tenant;
class FutureTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
/** @test */
public function keys_can_be_deleted_from_tenant_storage()
{
$tenant = Tenant::new()->withData(['email' => 'foo@example.com', 'role' => 'admin'])->save();
$this->assertArrayHasKey('email', $tenant->data);
$tenant->deleteKey('email');
$this->assertArrayNotHasKey('email', $tenant->data);
$this->assertArrayNotHasKey('email', tenancy()->all()->first()->data);
$tenant->put(['foo' => 'bar', 'abc' => 'xyz']);
$this->assertArrayHasKey('foo', $tenant->data);
$this->assertArrayHasKey('abc', $tenant->data);
$tenant->deleteKeys(['foo', 'abc']);
$this->assertArrayNotHasKey('foo', $tenant->data);
$this->assertArrayNotHasKey('abc', $tenant->data);
}
/** @test */
public function tenant_can_be_identified_using_an_arbitrary_string()
{
if (! tenancy()->storage instanceof CanFindByAnyKey) {
$this->markTestSkipped(get_class(tenancy()->storage) . ' does not implement the CanFindByAnyKey interface.');
}
$tenant = Tenant::new()->withData(['email' => 'foo@example.com'])->save();
$this->assertSame($tenant->id, tenancy()->findByEmail('foo@example.com')->id);
}
}

View file

@ -2,15 +2,28 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use GlobalCache;
use Stancl\Tenancy\Tenant;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Facades\GlobalCache;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class GlobalCacheTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
config(['tenancy.bootstrappers' => [
CacheTenancyBootstrapper::class,
]]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
}
/** @test */
public function global_cache_manager_stores_data_in_global_cache()
@ -19,28 +32,28 @@ class GlobalCacheTest extends TestCase
GlobalCache::put(['foo' => 'bar'], 1);
$this->assertSame('bar', GlobalCache::get('foo'));
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$tenant1 = Tenant::create();
tenancy()->initialize($tenant1);
$this->assertSame('bar', GlobalCache::get('foo'));
GlobalCache::put(['abc' => 'xyz'], 1);
cache(['def' => 'ghi'], 10);
$this->assertSame('ghi', cache('def'));
tenancy()->endTenancy();
tenancy()->end();
$this->assertSame('xyz', GlobalCache::get('abc'));
$this->assertSame('bar', GlobalCache::get('foo'));
$this->assertSame(null, cache('def'));
Tenant::new()->withDomains(['bar.localhost'])->save();
tenancy()->init('bar.localhost');
$tenant2 = Tenant::create();
tenancy()->initialize($tenant2);
$this->assertSame('xyz', GlobalCache::get('abc'));
$this->assertSame('bar', GlobalCache::get('foo'));
$this->assertSame(null, cache('def'));
cache(['def' => 'xxx'], 1);
$this->assertSame('xxx', cache('def'));
tenancy()->init('foo.localhost');
tenancy()->initialize($tenant1);
$this->assertSame('ghi', cache('def'));
}
}

View file

@ -1,8 +1,6 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -11,26 +9,46 @@ use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Event;
use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class QueueTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = true;
public $mockConsoleOutput = false;
/** @test */
public function queues_use_non_tenant_db_connection()
/** @var Valuestore */
protected $valuestore;
public function setUp(): void
{
// requires using the db driver
$this->markTestIncomplete();
parent::setUp();
config([
'tenancy.bootstrappers' => [
QueueTenancyBootstrapper::class,
],
'queue.default' => 'redis',
]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
$this->valuestore = Valuestore::make(__DIR__ . '/../Etc/tmp/queuetest.json')->flush();
}
/** @test */
public function tenancy_is_initialized_inside_queues()
public function tenant_id_is_passed_to_tenant_queues()
{
$this->loadLaravelMigrations(['--database' => 'tenant']);
$tenant = Tenant::create();
tenancy()->initialize($tenant);
Event::fake();
dispatch(new TestJob());
dispatch(new TestJob($this->valuestore));
Event::assertDispatched(JobProcessing::class, function ($event) {
return $event->job->payload()['tenant_id'] === tenant('id');
@ -38,40 +56,81 @@ class QueueTest extends TestCase
}
/** @test */
public function tenancy_is_not_initialized_in_non_tenant_queues()
public function tenant_id_is_not_passed_to_central_queues()
{
$this->loadLaravelMigrations(['--database' => 'tenant']);
$tenant = Tenant::create();
tenancy()->initialize($tenant);
Event::fake();
dispatch(new TestJob())->onConnection('central');
config(['queue.connections.central' => [
'driver' => 'sync',
'central' => true,
]]);
dispatch(new TestJob($this->valuestore))->onConnection('central');
Event::assertDispatched(JobProcessing::class, function ($event) {
return ! isset($event->job->payload()['tenant_id']);
});
}
/** @test */
public function tenancy_is_initialized_inside_queues()
{
$tenant = Tenant::create([
'id' => 'acme',
]);
tenancy()->initialize($tenant);
dispatch(new TestJob($this->valuestore));
$this->assertFalse($this->valuestore->has('tenant_id'));
$this->artisan('queue:work --once');
$this->assertSame('The current tenant id is: acme', $this->valuestore->get('tenant_id'));
}
/** @test */
public function the_tenant_used_by_the_job_doesnt_change_when_the_current_tenant_changes()
{
$tenant1 = Tenant::create([
'id' => 'acme',
]);
tenancy()->initialize($tenant1);
dispatch(new TestJob($this->valuestore));
$tenant2 = Tenant::create([
'id' => 'foobar',
]);
tenancy()->initialize($tenant2);
$this->assertFalse($this->valuestore->has('tenant_id'));
$this->artisan('queue:work --once');
$this->assertSame('The current tenant id is: acme', $this->valuestore->get('tenant_id'));
}
}
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
/** @var Valuestore */
protected $valuestore;
public function __construct(Valuestore $valuestore)
{
//
$this->valuestore = $valuestore;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
logger(json_encode(\DB::table('users')->get()));
$this->valuestore->put('tenant_id', "The current tenant id is: " . tenant('id'));
}
}

View file

@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Stancl\Tenancy\Tenant;
class ReidentificationTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = false;
/**
* These tests are run when a tenant is identified after another tenant has already been identified.
*/
/** @test */
public function storage_facade_roots_are_correct()
{
$originals = [];
foreach (config('tenancy.filesystem.disks') as $disk) {
$originals[$disk] = config("filesystems.disks.{$disk}.root");
}
tenancy()->init('test.localhost');
Tenant::new()->withDomains(['second.localhost'])->save();
tenancy()->init('second.localhost');
foreach (config('tenancy.filesystem.disks') as $disk) {
$suffix = config('tenancy.filesystem.suffix_base') . tenant('id');
$current_path_prefix = \Storage::disk($disk)->getAdapter()->getPathPrefix();
if ($override = config("tenancy.filesystem.root_override.{$disk}")) {
$correct_path_prefix = str_replace('%storage_path%', storage_path(), $override);
} else {
if ($base = $originals[$disk]) {
$correct_path_prefix = $base . "/$suffix/";
} else {
$correct_path_prefix = "$suffix/";
}
}
$this->assertSame($correct_path_prefix, $current_path_prefix);
}
}
/** @test */
public function storage_path_is_correct()
{
$original = storage_path();
tenancy()->init('test.localhost');
Tenant::new()->withDomains(['second.localhost'])->save();
tenancy()->init('second.localhost');
$suffix = config('tenancy.filesystem.suffix_base') . tenant('id');
$this->assertSame($original . "/$suffix", storage_path());
}
}

View file

@ -2,27 +2,28 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\TestCase;
class RequestDataIdentificationTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
config([
'tenancy.exempt_domains' => [
'tenancy.central_domains' => [
'localhost',
],
]);
InitializeTenancyByRequestData::$header = 'X-Tenant';
InitializeTenancyByRequestData::$queryParameter = 'tenant';
Route::middleware(InitializeTenancyByRequestData::class)->get('/test', function () {
return 'Tenant id: ' . tenant('id');
});
@ -31,11 +32,9 @@ class RequestDataIdentificationTest extends TestCase
/** @test */
public function header_identification_works()
{
$this->app->bind(InitializeTenancyByRequestData::class, function () {
return new InitializeTenancyByRequestData('X-Tenant');
});
$tenant = Tenant::new()->save();
$tenant2 = Tenant::new()->save();
InitializeTenancyByRequestData::$header = 'X-Tenant';
$tenant = Tenant::create();
$tenant2 = Tenant::create();
$this
->withoutExceptionHandling()
@ -48,11 +47,11 @@ class RequestDataIdentificationTest extends TestCase
/** @test */
public function query_parameter_identification_works()
{
$this->app->bind(InitializeTenancyByRequestData::class, function () {
return new InitializeTenancyByRequestData(null, 'tenant');
});
$tenant = Tenant::new()->save();
$tenant2 = Tenant::new()->save();
InitializeTenancyByRequestData::$header = null;
InitializeTenancyByRequestData::$queryParameter = 'tenant';
$tenant = Tenant::create();
$tenant2 = Tenant::create();
$this
->withoutExceptionHandling()

View file

@ -1,97 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Redis;
class TenancyBootstrappersTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = false;
/** @test */
public function database_connection_is_switched()
{
$old_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
$this->initTenancy();
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
$this->assertNotEquals($old_connection_name, $new_connection_name);
$this->assertEquals('tenant', $new_connection_name);
}
/** @test */
public function redis_is_prefixed()
{
if (! config('tenancy.redis.tenancy')) {
$this->markTestSkipped('Redis tenancy disabled.');
}
$this->initTenancy();
foreach (config('tenancy.redis.prefixed_connections', ['default']) as $connection) {
$prefix = config('tenancy.redis.prefix_base') . tenant('id');
$client = Redis::connection($connection)->client();
$this->assertEquals($prefix, $client->getOption($client::OPT_PREFIX));
}
}
/** @test */
public function filesystem_is_suffixed()
{
$old_storage_path = storage_path();
$old_storage_facade_roots = [];
foreach (config('tenancy.filesystem.disks') as $disk) {
$old_storage_facade_roots[$disk] = config("filesystems.disks.{$disk}.root");
}
$this->initTenancy();
$new_storage_path = storage_path();
$this->assertEquals($old_storage_path . '/' . config('tenancy.filesystem.suffix_base') . tenant('id'), $new_storage_path);
foreach (config('tenancy.filesystem.disks') as $disk) {
$suffix = config('tenancy.filesystem.suffix_base') . tenant('id');
$current_path_prefix = \Storage::disk($disk)->getAdapter()->getPathPrefix();
if ($override = config("tenancy.filesystem.root_override.{$disk}")) {
$correct_path_prefix = str_replace('%storage_path%', storage_path(), $override);
} else {
if ($base = $old_storage_facade_roots[$disk]) {
$correct_path_prefix = $base . "/$suffix/";
} else {
$correct_path_prefix = "$suffix/";
}
}
$this->assertSame($correct_path_prefix, $current_path_prefix);
}
}
/** @test */
public function cache_is_tagged()
{
$this->assertSame(['foo'], cache()->tags('foo')->getTags()->getNames());
$this->initTenancy();
$expected = [config('tenancy.cache.tag_base') . tenant('id'), 'foo', 'bar'];
$this->assertEquals($expected, cache()->tags(['foo', 'bar'])->getTags()->getNames());
}
/** @test */
public function the_default_db_connection_is_used_when_the_config_value_is_null()
{
$original = config('database.default');
tenancy()->create(['foo.localhost']);
tenancy()->init('foo.localhost');
$this->assertSame(null, config("database.connections.$original.foo"));
config(["database.connections.$original.foo" => 'bar']);
tenancy()->create(['bar.localhost']);
tenancy()->init('bar.localhost');
$this->assertSame('bar', config("database.connections.$original.foo"));
}
}

View file

@ -2,29 +2,58 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Stancl\Tenancy\Tenant;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Controllers\TenantAssetsController;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
use Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class TenantAssetTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
public function setUp(): void
{
parent::setUp();
config(['tenancy.bootstrappers' => [
FilesystemTenancyBootstrapper::class
]]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
}
public function tearDown(): void
{
parent::tearDown();
// Cleanup
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByDomain::class;
}
/** @test */
public function asset_can_be_accessed_using_the_url_returned_by_the_tenant_asset_helper()
{
Tenant::create('localhost');
tenancy()->init('localhost');
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class;
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$filename = 'testfile' . $this->randomString(10);
\Storage::disk('public')->put($filename, 'bar');
Storage::disk('public')->put($filename, 'bar');
$path = storage_path("app/public/$filename");
// response()->file() returns BinaryFileResponse whose content is
// inaccessible via getContent, so ->assertSee() can't be used
$this->assertFileExists($path);
$response = $this->get(tenant_asset($filename));
$response = $this->get(tenant_asset($filename), [
'X-Tenant' => $tenant->id,
]);
$response->assertSuccessful();
@ -40,8 +69,8 @@ class TenantAssetTest extends TestCase
{
config(['app.asset_url' => null]);
Tenant::create('foo.localhost');
tenancy()->init('foo.localhost');
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->assertSame(route('stancl.tenancy.asset', ['path' => 'foo']), asset('foo'));
}
@ -51,8 +80,8 @@ class TenantAssetTest extends TestCase
{
config(['app.asset_url' => 'https://an-s3-bucket']);
$tenant = Tenant::create(['foo.localhost']);
tenancy()->init('foo.localhost');
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->assertSame("https://an-s3-bucket/tenant{$tenant->id}/foo", asset('foo'));
}
@ -63,8 +92,8 @@ class TenantAssetTest extends TestCase
$original = global_asset('foobar');
$this->assertSame(asset('foobar'), global_asset('foobar'));
Tenant::create(['foo.localhost']);
tenancy()->init('foo.localhost');
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->assertSame($original, global_asset('foobar'));
}
@ -79,8 +108,8 @@ class TenantAssetTest extends TestCase
'tenancy.filesystem.asset_helper_tenancy' => false,
]);
Tenant::create('foo.localhost');
tenancy()->init('foo.localhost');
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->assertSame($original, asset('foo'));
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Tests\TestCase;
class TenantAwareCommandTest extends TestCase
{
/** @test */
public function commands_run_globally_are_tenant_aware_and_return_valid_exit_code()
{
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant2['id']],
]);
$this->artisan('user:add')
->assertExitCode(0);
tenancy()->initialize($tenant1);
$this->assertNotEmpty(DB::table('users')->get());
tenancy()->end();
tenancy()->initialize($tenant2);
$this->assertNotEmpty(DB::table('users')->get());
tenancy()->end();
}
}

View file

@ -1,227 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Mockery;
use Stancl\Tenancy\Contracts\StorageDriver;
use Stancl\Tenancy\Tenant;
use Tenancy;
class TenantClassTest extends TestCase
{
public $autoInitTenancy = false;
public $autoCreateTenant = false;
/** @test */
public function data_cache_works_properly()
{
// $spy = Mockery::spy(config('tenancy.storage_driver'))->makePartial();
// $this->instance(StorageDriver::class, $spy);
$tenant = Tenant::create(['foo.localhost'], ['foo' => 'bar']);
$this->assertSame('bar', $tenant->data['foo']);
$tenant->put('abc', 'xyz');
$this->assertSame('xyz', $tenant->data['abc']);
$tenant->put(['aaa' => 'bbb', 'ccc' => 'ddd']);
$this->assertSame('bbb', $tenant->data['aaa']);
$this->assertSame('ddd', $tenant->data['ccc']);
// $spy->shouldNotHaveReceived('get');
$this->assertSame(null, $tenant->dfuighdfuigfhdui);
// $spy->shouldHaveReceived('get')->once();
Mockery::close();
}
/** @test */
public function tenant_can_have_multiple_domains()
{
$tenant = Tenant::create(['foo.localhost', 'bar.localhost']);
$this->assertSame(['foo.localhost', 'bar.localhost'], $tenant->domains);
$this->assertSame($tenant->id, Tenancy::findByDomain('foo.localhost')->id);
$this->assertSame($tenant->id, Tenancy::findByDomain('bar.localhost')->id);
}
/** @test */
public function updating_a_tenant_works()
{
$id = 'abc' . $this->randomString();
$tenant = Tenant::create(['foo.localhost'], ['id' => $id]);
$tenant->foo = 'bar';
$tenant->save();
$this->assertEquals(['id' => $id, 'foo' => 'bar', '_tenancy_db_name' => $tenant->database()->getName()], $tenant->data);
$this->assertEquals(['id' => $id, 'foo' => 'bar', '_tenancy_db_name' => $tenant->database()->getName()], tenancy()->find($id)->data);
$tenant->addDomains('abc.localhost');
$tenant->save();
$this->assertEqualsCanonicalizing(['foo.localhost', 'abc.localhost'], $tenant->domains);
$this->assertEqualsCanonicalizing(['foo.localhost', 'abc.localhost'], tenancy()->find($id)->domains);
$tenant->removeDomains(['foo.localhost']);
$tenant->save();
$this->assertEqualsCanonicalizing(['abc.localhost'], $tenant->domains);
$this->assertEqualsCanonicalizing(['abc.localhost'], tenancy()->find($id)->domains);
$tenant->withDomains(['completely.localhost', 'different.localhost', 'domains.localhost']);
$tenant->save();
$this->assertEqualsCanonicalizing(['completely.localhost', 'different.localhost', 'domains.localhost'], $tenant->domains);
$this->assertEqualsCanonicalizing(['completely.localhost', 'different.localhost', 'domains.localhost'], tenancy()->find($id)->domains);
}
/** @test */
public function with_methods_work()
{
$id = 'foo' . $this->randomString();
$tenant = Tenant::new()->withDomains(['foo.localhost'])->with('id', $id);
$this->assertSame($id, $tenant->id);
$id2 = 'bar' . $this->randomString();
$tenant2 = Tenant::new()->withDomains(['bar.localhost'])->withId($id2)->withFooBar('xyz');
$this->assertSame($id2, $tenant2->data['id']);
$this->assertSame('xyz', $tenant2->foo_bar);
$this->assertArrayHasKey('foo_bar', $tenant2->data);
}
/** @test */
public function an_exception_is_thrown_when_an_unknown_method_is_called()
{
$tenant = Tenant::new();
$this->expectException(\BadMethodCallException::class);
$tenant->sdjigndfgnjdfgj();
}
/** @test */
public function tenant_data_can_be_set_during_creation()
{
Tenant::new()->withData(['foo' => 'bar'])->save();
$data = tenancy()->all()->first()->data;
unset($data['id']);
unset($data['_tenancy_db_name']);
$this->assertSame(['foo' => 'bar'], $data);
}
/** @test */
public function run_method_works()
{
$this->assertSame(null, tenancy()->getTenant());
$users_table_empty = function () {
return count(\DB::table('users')->get()) === 0;
};
$tenant = Tenant::new()->save();
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant->id],
]);
tenancy()->initialize($tenant);
$this->assertTrue($users_table_empty());
tenancy()->end();
$foo = $tenant->run(function () {
\DB::table('users')->insert([
'name' => 'foo',
'email' => 'foo@bar.xy',
'password' => bcrypt('secret'),
]);
return 'foo';
});
// test return value
$this->assertSame('foo', $foo);
// test that tenancy was ended
$this->assertSame(false, tenancy()->initialized);
$this->assertSame(null, tenancy()->getTenant());
// test closure
tenancy()->initialize($tenant);
$this->assertFalse($users_table_empty());
// test returning to original tenant
$tenant2 = Tenant::new()->save();
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant2->id],
]);
tenancy()->initialize($tenant2);
$this->assertSame($tenant2, tenancy()->getTenant());
$this->assertTrue($users_table_empty());
$tenant->run(function () {
\DB::table('users')->insert([
'name' => 'bar',
'email' => 'bar@bar.xy',
'password' => bcrypt('secret'),
]);
});
$this->assertSame($tenant2, tenancy()->getTenant());
$this->assertSame(2, $tenant->run(function () {
return \DB::table('users')->count();
}));
// test that the tenant variable can be accessed
$this->assertSame($tenant->id, $tenant->run(function ($tenant) {
return $tenant->id;
}));
}
/** @test */
public function extra_config_is_merged_into_the_connection_config_array()
{
$tenant = Tenant::new()->withData([
'_tenancy_db_link' => 'foo',
'_tenancy_db_name' => 'dbname',
'_tenancy_db_username' => 'usernamefoo',
'_tenancy_db_password' => 'passwordfoo',
'_tenancy_db_connection' => 'mysql',
]);
config(['database.connections.mysql' => [
'driver' => 'mysql',
'url' => null,
'host' => 'mysql',
'port' => '3306',
'database' => 'main',
'username' => 'root',
'password' => 'password',
'unix_socket' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => [],
]]);
$this->assertEquals([
'database' => 'dbname',
'username' => 'usernamefoo',
'password' => 'passwordfoo',
'link' => 'foo',
'driver' => 'mysql',
'url' => null,
'host' => 'mysql',
'port' => '3306',
'unix_socket' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => [],
], $tenant->database()->connection());
}
}

View file

@ -2,44 +2,50 @@
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Support\Facades\Queue;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter;
use Stancl\Tenancy\Tenant;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\DatabaseManager;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\Listeners\JobPipeline;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager;
use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager;
use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager;
use Stancl\Tenancy\Tests\TestCase;
class TenantDatabaseManagerTest extends TestCase
{
public $autoInitTenancy = false;
/**
* @test
* @dataProvider database_manager_provider
*/
public function databases_can_be_created_and_deleted($driver, $databaseManager)
{
if (! $this->isContainerized()) {
$this->markTestSkipped('As to not bloat your computer with test databases, this test is not run by default.');
}
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
config()->set([
"tenancy.database_managers.$driver" => $databaseManager,
'tenancy.internal_prefix' => 'tenancy_',
]);
$name = 'db' . $this->randomString();
$tenant = Tenant::new()->withData([
'_tenancy_db_name' => $name,
'_tenancy_db_connection' => $driver,
]);
$this->assertFalse(app($databaseManager)->databaseExists($name));
$tenant->save(); // generate credentials & create DB
$tenant = Tenant::create([
'tenancy_db_name' => $name,
'tenancy_db_connection' => $driver,
]);
$this->assertTrue(app($databaseManager)->databaseExists($name));
app($databaseManager)->deleteDatabase($tenant);
$this->assertFalse(app($databaseManager)->databaseExists($name));
@ -50,60 +56,34 @@ class TenantDatabaseManagerTest extends TestCase
{
$this->assertSame('central', config('database.default'));
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
// todo if the prefix is _tenancy_, this blows up. write a tenantmodel test that the prefix can be _tenancy_
config(['tenancy.internal_prefix' => 'tenancy_',]);
$database = 'db' . $this->randomString();
$tenant = Tenant::new()->withData([
'_tenancy_db_name' => $database,
'_tenancy_db_connection' => 'mysql',
]);
$this->assertFalse(app(MySQLDatabaseManager::class)->databaseExists($database));
$tenant->save(); // create DB
Tenant::create([
'tenancy_db_name' => $database,
'tenancy_db_connection' => 'mysql',
]);
$this->assertTrue(app(MySQLDatabaseManager::class)->databaseExists($database));
$database = 'db' . $this->randomString();
$tenant = Tenant::new()->withData([
'_tenancy_db_name' => $database,
'_tenancy_db_connection' => 'pgsql',
]);
$this->assertFalse(app(PostgreSQLDatabaseManager::class)->databaseExists($database));
$tenant->save(); // create DB
Tenant::create([
'tenancy_db_name' => $database,
'tenancy_db_connection' => 'pgsql',
]);
$this->assertTrue(app(PostgreSQLDatabaseManager::class)->databaseExists($database));
}
/**
* @test
* @dataProvider database_manager_provider
*/
public function databases_can_be_created_and_deleted_using_queued_commands($driver, $databaseManager)
{
if (! $this->isContainerized()) {
$this->markTestSkipped('As to not bloat your computer with test databases, this test is not run by default.');
}
config()->set([
'database.default' => $driver,
"tenancy.database_managers.$driver" => $databaseManager,
]);
$name = 'db' . $this->randomString();
$tenant = Tenant::new()->withData([
'_tenancy_db_name' => $name,
'_tenancy_db_connection' => $driver,
]);
$tenant->database()->makeCredentials();
$this->assertFalse(app($databaseManager)->databaseExists($name));
$job = new QueuedTenantDatabaseCreator(app($databaseManager), $tenant);
$job->handle();
$this->assertTrue(app($databaseManager)->databaseExists($name));
$job = new QueuedTenantDatabaseDeleter(app($databaseManager), $tenant);
$job->handle();
$this->assertFalse(app($databaseManager)->databaseExists($name));
}
public function database_manager_provider()
{
return [
@ -116,30 +96,49 @@ class TenantDatabaseManagerTest extends TestCase
}
/** @test */
public function database_creation_can_be_queued()
public function db_name_is_prefixed_with_db_path_when_sqlite_is_used()
{
Queue::fake();
config()->set([
'tenancy.queue_database_creation' => true,
if (file_exists(database_path('foodb'))) {
unlink(database_path('foodb')); // cleanup
}
config([
'database.connections.fooconn.driver' => 'sqlite',
'tenancy.internal_prefix' => 'tenancy_',
]);
Tenant::create(['test2.localhost']);
Queue::assertPushed(QueuedTenantDatabaseCreator::class);
$tenant = Tenant::create([
'tenancy_db_name' => 'foodb',
'tenancy_db_connection' => 'fooconn',
]);
app(DatabaseManager::class)->createTenantConnection($tenant);
$this->assertSame(config('database.connections.tenant.database'), database_path('foodb'));
}
/** @test */
public function database_deletion_can_be_queued()
public function schema_manager_uses_schema_to_separate_tenant_dbs()
{
Queue::fake();
$tenant = Tenant::create(['test2.localhost']);
config()->set([
'tenancy.queue_database_deletion' => true,
'tenancy.delete_database_after_tenant_deletion' => true,
config([
'tenancy.database_managers.pgsql' => \Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class,
'tenancy.boostrappers' => [
DatabaseTenancyBootstrapper::class,
],
]);
$tenant->delete();
Queue::assertPushed(QueuedTenantDatabaseDeleter::class);
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
$originalDatabaseName = config(['database.connections.pgsql.database']);
$tenant = Tenant::create([
'tenancy_db_connection' => 'pgsql',
]);
tenancy()->initialize($tenant);
$this->assertSame($tenant->database()->getName(), config('database.connections.' . config('database.default') . '.schema'));
$this->assertSame($originalDatabaseName, config(['database.connections.pgsql.database']));
}
}

View file

@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Stancl\Tenancy\Tenant;
use Tenancy;
class TenantManagerEventsTest extends TestCase
{
public $autoInitTenancy = false;
/** @test */
public function bootstrapping_event_works()
{
$id = Tenant::new()->withDomains(['foo.localhost'])->save()['id'];
Tenancy::eventListener('bootstrapping', function ($tenantManager) use ($id) {
if ($tenantManager->getTenant('id') === $id) {
config(['tenancy.foo' => 'bar']);
}
});
$this->assertSame(null, config('tenancy.foo'));
tenancy()->init('foo.localhost');
$this->assertSame('bar', config('tenancy.foo'));
}
/** @test */
public function bootstrapped_event_works()
{
$id = Tenant::new()->withDomains(['foo.localhost'])->save()['id'];
Tenancy::eventListener('bootstrapped', function ($tenantManager) use ($id) {
if ($tenantManager->getTenant('id') === $id) {
config(['tenancy.foo' => 'bar']);
}
});
$this->assertSame(null, config('tenancy.foo'));
tenancy()->init('foo.localhost');
$this->assertSame('bar', config('tenancy.foo'));
}
/** @test */
public function ending_event_works()
{
$id = Tenant::new()->withDomains(['foo.localhost'])->save()['id'];
Tenancy::eventListener('ending', function ($tenantManager) use ($id) {
if ($tenantManager->getTenant('id') === $id) {
config(['tenancy.foo' => 'bar']);
}
});
$this->assertSame(null, config('tenancy.foo'));
tenancy()->init('foo.localhost');
$this->assertSame(null, config('tenancy.foo'));
tenancy()->endTenancy();
$this->assertSame('bar', config('tenancy.foo'));
}
/** @test */
public function ended_event_works()
{
Tenant::new()->withDomains(['foo.localhost'])->save()['id'];
Tenancy::eventListener('ended', function ($tenantManager) {
config(['tenancy.foo' => 'bar']);
});
$this->assertSame(null, config('tenancy.foo'));
tenancy()->init('foo.localhost');
$this->assertSame(null, config('tenancy.foo'));
tenancy()->endTenancy();
$this->assertSame('bar', config('tenancy.foo'));
}
/** @test */
public function database_can_be_reconnected_using_event_hooks()
{
config(['database.connections.tenantabc' => [
'driver' => 'sqlite',
'database' => database_path('some_special_database.sqlite'),
]]);
$id = Tenant::create('abc.localhost')['id'];
Tenancy::eventListener('bootstrapping', function ($tenancy) use ($id) {
if ($tenancy->getTenant()['id'] === $id) {
$tenancy->database->switchConnection('tenantabc');
return ['database'];
}
});
$this->assertNotSame('tenantabc', \DB::connection()->getConfig()['name']);
tenancy()->init('abc.localhost');
$this->assertSame('tenantabc', \DB::connection()->getConfig()['name']);
}
/** @test */
public function database_cannot_be_reconnected_without_using_prevents()
{
config(['database.connections.tenantabc' => [
'driver' => 'sqlite',
'database' => database_path('some_special_database.sqlite'),
]]);
$id = Tenant::create('abc.localhost')['id'];
Tenancy::eventListener('bootstrapping', function ($tenancy) use ($id) {
if ($tenancy->getTenant()['id'] === $id) {
$tenancy->database->switchConnection('tenantabc');
// return ['database'];
}
});
$this->assertNotSame('tenantabc', \DB::connection()->getConfig()['name']);
tenancy()->init('abc.localhost');
$this->assertSame('tenant', \DB::connection()->getConfig()['name']);
}
/** @test */
public function tenant_is_persisted_before_the_created_hook_is_called()
{
$was_persisted = false;
Tenancy::eventListener('tenant.created', function ($tenancy, $tenant) use (&$was_persisted) {
$was_persisted = $tenant->persisted;
});
Tenant::new()->save();
$this->assertTrue($was_persisted);
}
}

View file

@ -1,383 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Stancl\Tenancy\Exceptions\DomainsOccupiedByOtherTenantException;
use Stancl\Tenancy\Exceptions\TenantDoesNotExistException;
use Stancl\Tenancy\Exceptions\TenantWithThisIdAlreadyExistsException;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseMigrator;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseSeeder;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\TenantManager;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
class TenantManagerTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
/** @test */
public function current_tenant_can_be_retrieved_using_getTenant()
{
$tenant = Tenant::new()->withDomains(['test2.localhost'])->save();
tenancy()->init('test2.localhost');
$this->assertEquals($tenant, tenancy()->getTenant());
}
/** @test */
public function initById_works()
{
$tenant = Tenant::new()->withDomains(['foo.localhost'])->save();
$this->assertNotEquals($tenant, tenancy()->getTenant());
tenancy()->initById($tenant['id']);
$this->assertEquals($tenant, tenancy()->getTenant());
}
/** @test */
public function findByDomain_works()
{
$tenant = Tenant::new()->withDomains(['foo.localhost'])->save();
$this->assertEquals($tenant, tenancy()->findByDomain('foo.localhost'));
}
/** @test */
public function find_works()
{
Tenant::new()->withDomains(['dev.localhost'])->save();
tenancy()->init('dev.localhost');
$this->assertEquals(tenant(), tenancy()->find(tenant('id')));
}
/** @test */
public function findByDomain_throws_an_exception_when_an_unused_domain_is_supplied()
{
$this->expectException(\Exception::class);
tenancy()->findByDomain('nonexistent.domain');
}
/** @test */
public function tenancy_can_be_ended()
{
$originals = [
'databaseName' => DB::connection()->getDatabaseName(),
'storage_path' => storage_path(),
'storage_root' => Storage::disk('local')->getAdapter()->getPathPrefix(),
'cache' => app('cache'),
];
// Verify that these assertions are the right way for testing this
$this->assertSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertSame($originals['storage_path'], storage_path());
$this->assertSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertSame($originals['cache'], app('cache'));
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$this->assertNotSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertNotSame($originals['storage_path'], storage_path());
$this->assertNotSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertNotSame($originals['cache'], app('cache'));
tenancy()->endTenancy();
$this->assertSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertSame($originals['storage_path'], storage_path());
$this->assertSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertSame($originals['cache'], app('cache'));
}
/** @test */
public function tenancy_can_be_ended_after_reidentification()
{
$originals = [
'databaseName' => DB::connection()->getDatabaseName(),
'storage_path' => storage_path(),
'storage_root' => Storage::disk('local')->getAdapter()->getPathPrefix(),
'cache' => app('cache'),
];
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
$this->assertNotSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertNotSame($originals['storage_path'], storage_path());
$this->assertNotSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertNotSame($originals['cache'], app('cache'));
tenancy()->endTenancy();
$this->assertSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertSame($originals['storage_path'], storage_path());
$this->assertSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertSame($originals['cache'], app('cache'));
// Reidentify tenant
Tenant::new()->withDomains(['bar.localhost'])->save();
tenancy()->init('bar.localhost');
$this->assertNotSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertNotSame($originals['storage_path'], storage_path());
$this->assertNotSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertNotSame($originals['cache'], app('cache'));
tenancy()->endTenancy();
$this->assertSame($originals['databaseName'], DB::connection()->getDatabaseName());
$this->assertSame($originals['storage_path'], storage_path());
$this->assertSame($originals['storage_root'], Storage::disk('local')->getAdapter()->getPathPrefix());
$this->assertSame($originals['cache'], app('cache'));
}
/** @test */
public function tenant_can_be_deleted()
{
$tenant = Tenant::new()->withDomains(['foo.localhost'])->save();
$this->assertEquals([$tenant], tenancy()->all()->toArray());
$tenant->delete();
$this->assertEquals([], tenancy()->all()->toArray());
}
/** @test */
public function all_returns_a_list_of_all_tenants()
{
$tenant1 = Tenant::new()->withDomains(['foo.localhost'])->save();
$tenant2 = Tenant::new()->withDomains(['bar.localhost'])->save();
$this->assertEqualsCanonicalizing([$tenant1, $tenant2], tenancy()->all()->toArray());
}
/** @test */
public function data_can_be_passed_in_the_create_method()
{
$data = ['plan' => 'free', 'subscribed_until' => '2020-01-01'];
$tenant = Tenant::create(['foo.localhost'], $data);
$tenant_data = $tenant->data;
unset($tenant_data['id']);
unset($tenant_data['_tenancy_db_name']);
$this->assertSame($data, $tenant_data);
}
/** @test */
public function database_name_can_be_passed_in_the_create_method()
{
$database = 'abc' . $this->randomString();
$tenant = tenancy()->create(['foo.localhost'], [
'_tenancy_db_name' => $database,
]);
$this->assertSame($database, $tenant->database()->getName());
}
/** @test */
public function id_cannot_be_changed()
{
$tenant = Tenant::create(['test2.localhost']);
$this->expectException(\Stancl\Tenancy\Exceptions\TenantStorageException::class);
$tenant->id = 'bar';
$tenant2 = Tenant::create(['test3.localhost']);
$this->expectException(\Stancl\Tenancy\Exceptions\TenantStorageException::class);
$tenant2->put('id', 'foo');
}
/** @test */
public function all_returns_a_collection_of_tenant_objects()
{
Tenant::create('foo.localhost');
$this->assertSame('Tenant', class_basename(tenancy()->all()[0]));
}
/** @test */
public function Tenant_is_bound_correctly_to_the_service_container()
{
$this->assertSame(null, app(Tenant::class));
$tenant = Tenant::create(['foo.localhost']);
app(TenantManager::class)->initializeTenancy($tenant);
$this->assertSame($tenant->id, app(Tenant::class)->id);
$this->assertSame(app(Tenant::class), app(TenantManager::class)->getTenant());
app(TenantManager::class)->endTenancy();
$this->assertSame(app(Tenant::class), app(TenantManager::class)->getTenant());
}
/** @test */
public function id_can_be_supplied_during_creation()
{
$id = 'abc' . $this->randomString();
$this->assertSame($id, Tenant::create(['foo.localhost'], ['id' => $id])->id);
$this->assertTrue(tenancy()->all()->contains(function ($tenant) use ($id) {
return $tenant->id === $id;
}));
}
/** @test */
public function automatic_migrations_work()
{
$tenant = Tenant::create(['foo.localhost']);
tenancy()->initialize($tenant);
$this->assertFalse(\Schema::hasTable('users'));
config(['tenancy.migrate_after_creation' => true]);
$tenant2 = Tenant::create(['bar.localhost']);
tenancy()->initialize($tenant2);
$this->assertTrue(\Schema::hasTable('users'));
}
/** @test */
public function automatic_seeding_works()
{
config(['tenancy.migrate_after_creation' => true]);
$tenant = Tenant::create(['foo.localhost']);
tenancy()->initialize($tenant);
$this->assertSame(0, \DB::table('users')->count());
config([
'tenancy.seed_after_migration' => true,
'tenancy.seeder_parameters' => [
'--class' => ExampleSeeder::class,
],
]);
$tenant2 = Tenant::create(['bar.localhost']);
tenancy()->initialize($tenant2);
$this->assertSame(1, \DB::table('users')->count());
}
/** @test */
public function ensureTenantCanBeCreated_works()
{
$id = 'foo' . $this->randomString();
Tenant::create(['foo.localhost'], ['id' => $id]);
$this->expectException(DomainsOccupiedByOtherTenantException::class);
Tenant::create(['foo.localhost']);
$this->expectException(TenantWithThisIdAlreadyExistsException::class);
Tenant::create(['bar.localhost'], ['id' => $id]);
}
/** @test */
public function automigration_is_queued_when_db_creation_is_queued()
{
Queue::fake();
config([
'tenancy.queue_database_creation' => true,
'tenancy.migrate_after_creation' => true,
]);
$tenant = Tenant::new()->save();
Queue::assertPushedWithChain(QueuedTenantDatabaseCreator::class, [
QueuedTenantDatabaseMigrator::class,
]);
// foreach (Queue::pushedJobs() as $job) {
// $job[0]['job']->handle(); // this doesn't execute the chained job
// }
// tenancy()->initialize($tenant);
// $this->assertTrue(\Schema::hasTable('users'));
}
/** @test */
public function autoseeding_is_queued_when_db_creation_is_queued()
{
Queue::fake();
config([
'tenancy.queue_database_creation' => true,
'tenancy.migrate_after_creation' => true,
'tenancy.seed_after_migration' => true,
]);
Tenant::new()->save();
Queue::assertPushedWithChain(QueuedTenantDatabaseCreator::class, [
QueuedTenantDatabaseMigrator::class,
QueuedTenantDatabaseSeeder::class,
]);
}
/** @test */
public function TenantDoesNotExistException_is_thrown_when_find_is_called_on_an_id_that_does_not_belong_to_any_tenant()
{
$this->expectException(TenantDoesNotExistException::class);
tenancy()->find('gjnfdgf');
}
/** @test */
public function event_listeners_can_accept_arguments()
{
tenancy()->hook('tenant.creating', function ($tenantManager, $tenant) {
$this->assertSame('bar', $tenant->foo);
});
Tenant::new()->withData(['foo' => 'bar'])->save();
}
/** @test */
public function tenant_creating_hook_can_be_used_to_modify_tenants_data()
{
tenancy()->hook('tenant.creating', function ($tm, Tenant $tenant) {
$tenant->put([
'foo' => 'bar',
'abc123' => 'def456',
]);
});
$tenant = Tenant::new()->save();
$this->assertArrayHasKey('foo', $tenant->data);
$this->assertArrayHasKey('abc123', $tenant->data);
}
/** @test */
public function database_creation_can_be_disabled()
{
config(['tenancy.create_database' => false]);
tenancy()->hook('database.creating', function () {
$this->fail();
});
$tenant = Tenant::new()->save();
$this->assertTrue(true);
}
/** @test */
public function database_creation_can_be_disabled_for_specific_tenants()
{
config(['tenancy.create_database' => true]);
tenancy()->hook('database.creating', function () {
$this->assertTrue(true);
});
$tenant = Tenant::new()->save();
tenancy()->hook('database.creating', function () {
$this->fail();
});
$tenant2 = Tenant::new()->withData([
'_tenancy_create_database' => false,
])->save();
}
}

View file

@ -12,6 +12,7 @@ use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Tests\TestCase;
use Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator;
use Stancl\Tenancy\Contracts;
use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
class TenantModelTest extends TestCase
{
@ -34,7 +35,7 @@ class TenantModelTest extends TestCase
tenancy()->initialize($tenant);
$this->assertSame($tenant->id, app(Tenant::class)->id);
$this->assertSame($tenant->id, app(Contracts\Tenant::class)->id);
tenancy()->end();
@ -100,10 +101,10 @@ class TenantModelTest extends TestCase
$table->bigIncrements('id')->change();
});
config(['tenancy.id_generator' => null]);
unset(app()[UniqueIdentifierGenerator::class]);
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant1 = MyTenant::create();
$tenant2 = MyTenant::create();
$this->assertSame(1, $tenant1->id);
$this->assertSame(2, $tenant2->id);
@ -137,6 +138,7 @@ class TenantModelTest extends TestCase
class MyTenant extends Tenant
{
protected $table = 'tenants';
public $increments = true;
}
class AnotherTenant extends Model implements Contracts\Tenant
@ -153,4 +155,9 @@ class AnotherTenant extends Model implements Contracts\Tenant
{
return $this->getAttribute('id');
}
public function run(callable $callback)
{
$callback();
}
}

View file

@ -1,197 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Stancl\Tenancy\StorageDrivers\Database\TenantRepository;
use Stancl\Tenancy\Tenant;
class TenantStorageTest extends TestCase
{
public $autoCreateTenant = true;
public $autoInitTenancy = true;
/** @test */
public function deleting_a_tenant_works()
{
$abc = Tenant::new()->withDomains(['abc.localhost'])->save();
$exists = function () use ($abc) {
return tenancy()->all()->contains(function ($tenant) use ($abc) {
return $tenant->id === $abc->id;
});
};
$this->assertTrue($exists());
$abc->delete();
$this->assertFalse($exists());
}
/** @test */
public function set_is_a_working_alias_for_put()
{
tenant()->set('foo', 'bar');
$this->assertSame('bar', tenant()->get('foo'));
}
/** @test */
public function put_works_with_key_and_value_as_separate_args()
{
tenant()->put('foo', 'bar');
$this->assertSame('bar', tenant()->get('foo'));
}
/** @test */
public function put_works_with_key_and_value_as_a_single_arg()
{
$keys = ['foo', 'abc'];
$vals = ['bar', 'xyz'];
$data = array_combine($keys, $vals);
tenant()->put($data);
$this->assertSame($data, tenant()->get($keys));
}
/** @test */
public function put_on_the_current_tenant_pushes_the_value_into_the_tenant_property_array()
{
tenant()->put('foo', 'bar');
$this->assertSame('bar', tenancy()->getTenant('foo'));
}
/** @test */
public function arrays_can_be_stored()
{
tenant()->put('foo', [1, 2]);
$this->assertSame([1, 2], tenant()->get('foo'));
}
/** @test */
public function associative_arrays_can_be_stored()
{
$data = ['a' => 'b', 'c' => 'd'];
tenant()->put('foo', $data);
$this->assertSame($data, tenant()->get('foo'));
}
/** @test */
public function correct_storage_driver_is_used()
{
if (config('tenancy.storage_driver') == 'db') {
$this->assertSame('DatabaseStorageDriver', class_basename(tenancy()->storage));
} elseif (config('tenancy.storage_driver') == 'redis') {
$this->assertSame('RedisStorageDriver', class_basename(tenancy()->storage));
} else {
dd(class_basename(config('tenancy.storage_driver')));
}
}
/** @test */
public function data_is_stored_with_correct_data_types()
{
tenant()->put('someBool', false);
$this->assertSame('boolean', gettype(tenant()->get('someBool')));
$this->assertSame('boolean', gettype(tenant()->get(['someBool'])['someBool']));
tenant()->put('someInt', 5);
$this->assertSame('integer', gettype(tenant()->get('someInt')));
$this->assertSame('integer', gettype(tenant()->get(['someInt'])['someInt']));
tenant()->put('someDouble', 11.40);
$this->assertSame('double', gettype(tenant()->get('someDouble')));
$this->assertSame('double', gettype(tenant()->get(['someDouble'])['someDouble']));
tenant()->put('string', 'foo');
$this->assertSame('string', gettype(tenant()->get('string')));
$this->assertSame('string', gettype(tenant()->get(['string'])['string']));
}
/** @test */
public function tenant_repository_uses_correct_connection()
{
config(['database.connections.foo' => config('database.connections.sqlite')]);
config(['tenancy.storage_drivers.db.connection' => 'foo']);
$this->assertSame('foo', app(TenantRepository::class)->database->getName());
}
/** @test */
public function retrieving_data_without_cache_works()
{
Tenant::new()->withDomains(['foo.localhost'])->save();
tenancy()->init('foo.localhost');
tenant()->put('foo', 'bar');
$this->assertSame('bar', tenant()->get('foo'));
$this->assertSame(['foo' => 'bar'], tenant()->get(['foo']));
tenancy()->endTenancy();
tenancy()->init('foo.localhost');
$this->assertSame('bar', tenant()->get('foo'));
$this->assertSame(['foo' => 'bar'], tenant()->get(['foo']));
}
/** @test */
public function custom_columns_work_with_db_storage_driver()
{
if (config('tenancy.storage_driver') != 'db') {
$this->markTestSkipped();
}
tenancy()->endTenancy();
$this->loadMigrationsFrom([
'--path' => __DIR__ . '/Etc',
'--database' => 'central',
]);
config(['database.default' => 'sqlite']); // fix issue caused by loadMigrationsFrom
config(['tenancy.storage_drivers.db.custom_columns' => [
'foo',
]]);
tenancy()->create(['foo.localhost']);
tenancy()->init('foo.localhost');
tenant()->put('foo', '111');
$this->assertSame('111', tenant()->get('foo'));
tenant()->put(['foo' => 'bar', 'abc' => 'xyz']);
$this->assertSame(['foo' => 'bar', 'abc' => 'xyz'], tenant()->get(['foo', 'abc']));
$this->assertSame('bar', \DB::connection('central')->table('tenants')->where('id', tenant('id'))->first()->foo);
}
/** @test */
public function custom_columns_can_be_used_on_tenant_create()
{
if (config('tenancy.storage_driver') != 'db') {
$this->markTestSkipped();
}
tenancy()->endTenancy();
$this->loadMigrationsFrom([
'--path' => __DIR__ . '/Etc',
'--database' => 'central',
]);
config(['database.default' => 'sqlite']); // fix issue caused by loadMigrationsFrom
config(['tenancy.storage_drivers.db.custom_columns' => [
'foo',
]]);
tenancy()->create(['foo.localhost'], ['foo' => 'bar', 'abc' => 'xyz']);
tenancy()->init('foo.localhost');
$this->assertSame(['foo' => 'bar', 'abc' => 'xyz'], tenant()->get(['foo', 'abc']));
$this->assertSame('bar', \DB::connection('central')->table('tenants')->where('id', tenant('id'))->first()->foo);
}
}

View file

@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Tests\Traits;
use Stancl\Tenancy\Tenant;
use Stancl\Tenancy\Tests\TestCase;
class TenantAwareCommandTest extends TestCase
{
public $autoCreateTenant = false;
public $autoInitTenancy = false;
/** @test */
public function commands_run_globally_are_tenant_aware_and_return_valid_exit_code()
{
$tenant1 = Tenant::new()->save();
$tenant2 = Tenant::new()->save();
\Artisan::call('tenants:migrate', [
'--tenants' => [$tenant1['id'], $tenant2['id']],
]);
$this->artisan('user:add')
->assertExitCode(0);
tenancy()->initializeTenancy($tenant1);
$this->assertNotEmpty(\DB::table('users')->get());
tenancy()->end();
tenancy()->initializeTenancy($tenant2);
$this->assertNotEmpty(\DB::table('users')->get());
tenancy()->end();
}
}

View file

@ -1 +0,0 @@
// test DB creation, migration, seeding

View file

@ -1,136 +0,0 @@
<?php
namespace Stancl\Tenancy\Tests\v3;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Event;
use Spatie\Valuestore\Valuestore;
use Stancl\Tenancy\Database\Models\Tenant;
use Stancl\Tenancy\Events\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper;
use Stancl\Tenancy\Tests\TestCase;
class QueueTest extends TestCase
{
public $mockConsoleOutput = false;
/** @var Valuestore */
protected $valuestore;
public function setUp(): void
{
parent::setUp();
config([
'tenancy.bootstrappers' => [
QueueTenancyBootstrapper::class,
],
'queue.default' => 'redis',
]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
$this->valuestore = Valuestore::make(__DIR__ . '/../Etc/tmp/queuetest.json')->flush();
}
/** @test */
public function tenant_id_is_passed_to_tenant_queues()
{
$tenant = Tenant::create();
tenancy()->initialize($tenant);
Event::fake();
dispatch(new TestJob($this->valuestore));
Event::assertDispatched(JobProcessing::class, function ($event) {
return $event->job->payload()['tenant_id'] === tenant('id');
});
}
/** @test */
public function tenant_id_is_not_passed_to_central_queues()
{
$tenant = Tenant::create();
tenancy()->initialize($tenant);
Event::fake();
config(['queue.connections.central' => [
'driver' => 'sync',
'central' => true,
]]);
dispatch(new TestJob($this->valuestore))->onConnection('central');
Event::assertDispatched(JobProcessing::class, function ($event) {
return ! isset($event->job->payload()['tenant_id']);
});
}
/** @test */
public function tenancy_is_initialized_inside_queues()
{
$tenant = Tenant::create([
'id' => 'acme',
]);
tenancy()->initialize($tenant);
dispatch(new TestJob($this->valuestore));
$this->assertFalse($this->valuestore->has('tenant_id'));
$this->artisan('queue:work --once');
$this->assertSame('The current tenant id is: acme', $this->valuestore->get('tenant_id'));
}
/** @test */
public function the_tenant_used_by_the_job_doesnt_change_when_the_current_tenant_changes()
{
$tenant1 = Tenant::create([
'id' => 'acme',
]);
tenancy()->initialize($tenant1);
dispatch(new TestJob($this->valuestore));
$tenant2 = Tenant::create([
'id' => 'foobar',
]);
tenancy()->initialize($tenant2);
$this->assertFalse($this->valuestore->has('tenant_id'));
$this->artisan('queue:work --once');
$this->assertSame('The current tenant id is: acme', $this->valuestore->get('tenant_id'));
}
}
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** @var Valuestore */
protected $valuestore;
public function __construct(Valuestore $valuestore)
{
$this->valuestore = $valuestore;
}
public function handle()
{
$this->valuestore->put('tenant_id', "The current tenant id is: " . tenant('id'));
}
}