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

Refactor tests

This commit is contained in:
Samuel Štancl 2025-08-08 00:48:10 +02:00
parent 8b9b913c88
commit 889d58b787
2 changed files with 23 additions and 57 deletions

View file

@ -4,30 +4,18 @@ declare(strict_types=1);
use Stancl\Tenancy\Bootstrappers\DatabaseCacheBootstrapper; use Stancl\Tenancy\Bootstrappers\DatabaseCacheBootstrapper;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\JobPipeline\JobPipeline;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Tests\Etc\Tenant; use Stancl\Tenancy\Tests\Etc\Tenant;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
beforeEach(function () { use function Stancl\Tenancy\Tests\withBootstrapping;
Event::listen( use function Stancl\Tenancy\Tests\withCacheTables;
TenantCreated::class, use function Stancl\Tenancy\Tests\withTenantDatabases;
JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener()
);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class); beforeEach(function () {
Event::listen(TenancyEnded::class, RevertToCentralContext::class); withBootstrapping();
withCacheTables();
withTenantDatabases(true);
DatabaseCacheBootstrapper::$stores = null; DatabaseCacheBootstrapper::$stores = null;
@ -47,66 +35,38 @@ afterEach(function () {
}); });
test('DatabaseCacheBootstrapper switches the database cache store connections correctly', function () { test('DatabaseCacheBootstrapper switches the database cache store connections correctly', function () {
// Original connections (store and lock) are 'central' in the config
expect(config('cache.stores.database.connection'))->toBe('central'); expect(config('cache.stores.database.connection'))->toBe('central');
expect(config('cache.stores.database.lock_connection'))->toBe('central'); expect(config('cache.stores.database.lock_connection'))->toBe('central');
// The actual connection used by the cache store is 'central'
expect(Cache::store()->getConnection()->getName())->toBe('central'); expect(Cache::store()->getConnection()->getName())->toBe('central');
// Cache locks also use the 'central' connection
expect(Cache::lock('foo')->getConnectionName())->toBe('central'); expect(Cache::lock('foo')->getConnectionName())->toBe('central');
tenancy()->initialize(Tenant::create()); tenancy()->initialize(Tenant::create());
// Initializing tenancy should make both connections 'tenant'
expect(config('cache.stores.database.connection'))->toBe('tenant'); expect(config('cache.stores.database.connection'))->toBe('tenant');
expect(config('cache.stores.database.lock_connection'))->toBe('tenant'); expect(config('cache.stores.database.lock_connection'))->toBe('tenant');
// The actual connection used by the cache store and locks is now 'tenant'
// Purging the database cache store forces the CacheManager to resolve a new instance of
// the database store, using the connection names specified in the config ('tenant')
expect(Cache::store()->getConnection()->getName())->toBe('tenant'); expect(Cache::store()->getConnection()->getName())->toBe('tenant');
expect(Cache::lock('foo')->getConnectionName())->toBe('tenant'); expect(Cache::lock('foo')->getConnectionName())->toBe('tenant');
tenancy()->end(); tenancy()->end();
// Ending tenancy should change both connections back to the original ('central')
expect(config('cache.stores.database.connection'))->toBe('central'); expect(config('cache.stores.database.connection'))->toBe('central');
expect(config('cache.stores.database.lock_connection'))->toBe('central'); expect(config('cache.stores.database.lock_connection'))->toBe('central');
// The actual connection used by the cache store and the cache locks is now 'central' again
expect(Cache::store()->getConnection()->getName())->toBe('central'); expect(Cache::store()->getConnection()->getName())->toBe('central');
expect(Cache::lock('foo')->getConnectionName())->toBe('central'); expect(Cache::lock('foo')->getConnectionName())->toBe('central');
}); });
test('cache is separated correctly when using DatabaseCacheBootstrapper', function() { test('cache is separated correctly when using DatabaseCacheBootstrapper', function() {
// DB query for verifying that the cache is stored // We need the prefix later for lower-level assertions. Let's store it
// in the cache table of the current DB connection // once now and reuse this variable rather than re-fetching it to make
// under the passed key (the key in the DB is prefixed by the default cache prefix). // it clear that the scoping does NOT come from a prefix change.
$getCacheUsingDbQuery = function (string $cacheKey) {
// Prefix the cache key with the default cache prefix
$databaseCacheKey = config('cache.prefix') . $cacheKey;
return DB::selectOne("SELECT * FROM `cache` WHERE `key` = '{$databaseCacheKey}'")?->value; $cachePrefix = config('cache.prefix');
}; $getCacheUsingDbQuery = fn (string $cacheKey) =>
DB::selectOne("SELECT * FROM `cache` WHERE `key` = '{$cachePrefix}{$cacheKey}'")?->value;
// Create the cache table in the central DB
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
$tenant = Tenant::create(); $tenant = Tenant::create();
$tenant2 = Tenant::create(); $tenant2 = Tenant::create();
// Create the cache table in tenant DBs
// With DatabaseCacheBootstrapper, cache will be saved in these tenant DBs instead of the central DB
tenancy()->runForMultiple([$tenant, $tenant2], function () {
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
});
// Write to cache in central context // Write to cache in central context
cache()->set('foo', 'central'); cache()->set('foo', 'central');
expect(Cache::get('foo'))->toBe('central'); expect(Cache::get('foo'))->toBe('central');
@ -118,19 +78,16 @@ test('cache is separated correctly when using DatabaseCacheBootstrapper', functi
// Central cache doesn't leak to tenant context // Central cache doesn't leak to tenant context
expect(Cache::has('foo'))->toBeFalse(); expect(Cache::has('foo'))->toBeFalse();
// Verify that the 'foo' cache key doesn't exist in the tenant's DB using a direct DB query
expect($getCacheUsingDbQuery('foo'))->toBeNull(); expect($getCacheUsingDbQuery('foo'))->toBeNull();
cache()->set('foo', 'bar'); cache()->set('foo', 'bar');
expect(Cache::get('foo'))->toBe('bar'); expect(Cache::get('foo'))->toBe('bar');
// Verify that the 'foo' cache key is 'bar' using a direct DB query
expect($getCacheUsingDbQuery('foo'))->toContain('bar'); expect($getCacheUsingDbQuery('foo'))->toContain('bar');
tenancy()->initialize($tenant2); tenancy()->initialize($tenant2);
// Assert one tenant's cache doesn't leak to another tenant // Assert one tenant's cache doesn't leak to another tenant
expect(Cache::has('foo'))->toBeFalse(); expect(Cache::has('foo'))->toBeFalse();
// The 'foo' cache key in another tenant's database shouldn't exist
expect($getCacheUsingDbQuery('foo'))->toBeNull(); expect($getCacheUsingDbQuery('foo'))->toBeNull();
cache()->set('foo', 'xyz'); cache()->set('foo', 'xyz');
@ -151,7 +108,6 @@ test('cache is separated correctly when using DatabaseCacheBootstrapper', functi
}); });
test('DatabaseCacheBootstrapper auto-detects all database driver stores by default', function() { test('DatabaseCacheBootstrapper auto-detects all database driver stores by default', function() {
// Configure multiple stores with different drivers
config([ config([
'cache.stores.database' => [ 'cache.stores.database' => [
'driver' => 'database', 'driver' => 'database',

View file

@ -7,12 +7,22 @@ use Stancl\Tenancy\Tests\TestCase;
use Stancl\JobPipeline\JobPipeline; use Stancl\JobPipeline\JobPipeline;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Jobs\CreateDatabase; use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Events\TenantCreated; use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\MigrateDatabase; use Stancl\Tenancy\Jobs\MigrateDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
uses(TestCase::class)->in(__DIR__); uses(TestCase::class)->in(__DIR__);
function withBootstrapping()
{
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
}
function withTenantDatabases(bool $migrate = false) function withTenantDatabases(bool $migrate = false)
{ {
Event::listen(TenantCreated::class, JobPipeline::make($migrate Event::listen(TenantCreated::class, JobPipeline::make($migrate