mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 19:54:03 +00:00
Merge branch 'master' into cache-prefix
This commit is contained in:
commit
249fc545d2
22 changed files with 564 additions and 38 deletions
|
|
@ -3,8 +3,8 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Mail\MailManager;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Stancl\JobPipeline\JobPipeline;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Stancl\Tenancy\Bootstrappers\PrefixCacheTenancyBootstrapper;
|
||||
|
|
@ -12,23 +12,30 @@ use Stancl\Tenancy\Tests\Etc\Tenant;
|
|||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Events\TenantDeleted;
|
||||
use Stancl\Tenancy\Events\DeletingTenant;
|
||||
use Stancl\Tenancy\TenancyBroadcastManager;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Broadcasting\BroadcastManager;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Jobs\CreateStorageSymlinks;
|
||||
use Stancl\Tenancy\Jobs\RemoveStorageSymlinks;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Tests\Etc\TestingBroadcaster;
|
||||
use Stancl\Tenancy\Listeners\DeleteTenantStorage;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\UrlTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\MailTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use Stancl\Tenancy\CacheManager;
|
||||
|
||||
|
|
@ -333,6 +340,82 @@ test('local storage public urls are generated correctly', function() {
|
|||
expect(File::isDirectory($tenantStoragePath))->toBeFalse();
|
||||
});
|
||||
|
||||
test('BroadcastTenancyBootstrapper binds TenancyBroadcastManager to BroadcastManager and reverts the binding when tenancy is ended', function() {
|
||||
expect(app(BroadcastManager::class))->toBeInstanceOf(BroadcastManager::class);
|
||||
|
||||
tenancy()->initialize(Tenant::create());
|
||||
|
||||
expect(app(BroadcastManager::class))->toBeInstanceOf(TenancyBroadcastManager::class);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
expect(app(BroadcastManager::class))->toBeInstanceOf(BroadcastManager::class);
|
||||
});
|
||||
|
||||
test('BroadcastTenancyBootstrapper maps tenant broadcaster credentials to config as specified in the $credentialsMap property and reverts the config after ending tenancy', function() {
|
||||
config([
|
||||
'broadcasting.connections.testing.driver' => 'testing',
|
||||
'broadcasting.connections.testing.message' => $defaultMessage = 'default',
|
||||
]);
|
||||
|
||||
BroadcastTenancyBootstrapper::$credentialsMap = [
|
||||
'broadcasting.connections.testing.message' => 'testing_broadcaster_message',
|
||||
];
|
||||
|
||||
$tenant = Tenant::create(['testing_broadcaster_message' => $tenantMessage = 'first testing']);
|
||||
$tenant2 = Tenant::create(['testing_broadcaster_message' => $secondTenantMessage = 'second testing']);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
expect(array_key_exists('testing_broadcaster_message', tenant()->getAttributes()))->toBeTrue();
|
||||
expect(config('broadcasting.connections.testing.message'))->toBe($tenantMessage);
|
||||
|
||||
tenancy()->initialize($tenant2);
|
||||
|
||||
expect(config('broadcasting.connections.testing.message'))->toBe($secondTenantMessage);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
expect(config('broadcasting.connections.testing.message'))->toBe($defaultMessage);
|
||||
});
|
||||
|
||||
test('BroadcastTenancyBootstrapper makes the app use broadcasters with the correct credentials', function() {
|
||||
config([
|
||||
'broadcasting.default' => 'testing',
|
||||
'broadcasting.connections.testing.driver' => 'testing',
|
||||
'broadcasting.connections.testing.message' => $defaultMessage = 'default',
|
||||
]);
|
||||
|
||||
TenancyBroadcastManager::$tenantBroadcasters[] = 'testing';
|
||||
BroadcastTenancyBootstrapper::$credentialsMap = [
|
||||
'broadcasting.connections.testing.message' => 'testing_broadcaster_message',
|
||||
];
|
||||
|
||||
$registerTestingBroadcaster = fn() => app(BroadcastManager::class)->extend('testing', fn($app, $config) => new TestingBroadcaster($config['message']));
|
||||
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($defaultMessage);
|
||||
|
||||
$tenant = Tenant::create(['testing_broadcaster_message' => $tenantMessage = 'first testing']);
|
||||
$tenant2 = Tenant::create(['testing_broadcaster_message' => $secondTenantMessage = 'second testing']);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($tenantMessage);
|
||||
|
||||
tenancy()->initialize($tenant2);
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($secondTenantMessage);
|
||||
|
||||
tenancy()->end();
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($defaultMessage);
|
||||
});
|
||||
|
||||
test('MailTenancyBootstrapper maps tenant mail credentials to config as specified in the $credentialsMap property and makes the mailer use tenant credentials', function() {
|
||||
MailTenancyBootstrapper::$credentialsMap = [
|
||||
'mail.mailers.smtp.username' => 'smtp_username',
|
||||
|
|
@ -385,3 +468,48 @@ function getDiskPrefix(string $disk): string
|
|||
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
test('url bootstrapper overrides the root url when tenancy gets initialized and reverts the url to the central one after tenancy ends', function() {
|
||||
config(['tenancy.bootstrappers.url' => UrlTenancyBootstrapper::class]);
|
||||
|
||||
Route::group([
|
||||
'middleware' => InitializeTenancyBySubdomain::class,
|
||||
], function () {
|
||||
Route::get('/', function () {
|
||||
return true;
|
||||
})->name('home');
|
||||
});
|
||||
|
||||
$baseUrl = url(route('home'));
|
||||
config(['app.url' => $baseUrl]);
|
||||
|
||||
$rootUrlOverride = function (Tenant $tenant) use ($baseUrl) {
|
||||
$scheme = str($baseUrl)->before('://');
|
||||
$hostname = str($baseUrl)->after($scheme . '://');
|
||||
|
||||
return $scheme . '://' . $tenant->getTenantKey() . '.' . $hostname;
|
||||
};
|
||||
|
||||
UrlTenancyBootstrapper::$rootUrlOverride = $rootUrlOverride;
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenantUrl = $rootUrlOverride($tenant);
|
||||
|
||||
expect($tenantUrl)->not()->toBe($baseUrl);
|
||||
|
||||
expect(url(route('home')))->toBe($baseUrl);
|
||||
expect(URL::to('/'))->toBe($baseUrl);
|
||||
expect(config('app.url'))->toBe($baseUrl);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
expect(url(route('home')))->toBe($tenantUrl);
|
||||
expect(URL::to('/'))->toBe($tenantUrl);
|
||||
expect(config('app.url'))->toBe($tenantUrl);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
expect(url(route('home')))->toBe($baseUrl);
|
||||
expect(URL::to('/'))->toBe($baseUrl);
|
||||
expect(config('app.url'))->toBe($baseUrl);
|
||||
});
|
||||
|
|
|
|||
65
tests/BroadcastingTest.php
Normal file
65
tests/BroadcastingTest.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Stancl\Tenancy\TenancyBroadcastManager;
|
||||
use Illuminate\Broadcasting\BroadcastManager;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Tests\Etc\TestingBroadcaster;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
|
||||
|
||||
beforeEach(function() {
|
||||
withTenantDatabases();
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
});
|
||||
|
||||
test('bound broadcaster instance is the same before initializing tenancy and after ending it', function() {
|
||||
config(['broadcasting.default' => 'null']);
|
||||
TenancyBroadcastManager::$tenantBroadcasters[] = 'null';
|
||||
|
||||
$originalBroadcaster = app(BroadcasterContract::class);
|
||||
|
||||
tenancy()->initialize(Tenant::create());
|
||||
|
||||
// TenancyBroadcastManager binds new broadcaster
|
||||
$tenantBroadcaster = app(BroadcastManager::class)->driver();
|
||||
|
||||
expect($tenantBroadcaster)->not()->toBe($originalBroadcaster);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
expect($originalBroadcaster)->toBe(app(BroadcasterContract::class));
|
||||
});
|
||||
|
||||
test('new broadcasters get the channels from the previously bound broadcaster', function() {
|
||||
config([
|
||||
'broadcasting.default' => $driver = 'testing',
|
||||
'broadcasting.connections.testing.driver' => $driver,
|
||||
]);
|
||||
|
||||
TenancyBroadcastManager::$tenantBroadcasters[] = $driver;
|
||||
|
||||
$registerTestingBroadcaster = fn() => app(BroadcastManager::class)->extend('testing', fn($app, $config) => new TestingBroadcaster('testing'));
|
||||
$getCurrentChannels = fn() => array_keys(invade(app(BroadcastManager::class)->driver())->channels);
|
||||
|
||||
$registerTestingBroadcaster();
|
||||
Broadcast::channel($channel = 'testing-channel', fn() => true);
|
||||
|
||||
expect($channel)->toBeIn($getCurrentChannels());
|
||||
|
||||
tenancy()->initialize(Tenant::create());
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect($channel)->toBeIn($getCurrentChannels());
|
||||
|
||||
tenancy()->end();
|
||||
$registerTestingBroadcaster();
|
||||
|
||||
expect($channel)->toBeIn($getCurrentChannels());
|
||||
});
|
||||
25
tests/Etc/TestingBroadcaster.php
Normal file
25
tests/Etc/TestingBroadcaster.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Tests\Etc;
|
||||
|
||||
use Illuminate\Broadcasting\Broadcasters\Broadcaster;
|
||||
|
||||
class TestingBroadcaster extends Broadcaster {
|
||||
public function __construct(
|
||||
public string $message
|
||||
) {}
|
||||
|
||||
public function auth($request)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function validAuthenticationResponse($request, $result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function broadcast(array $channels, $event, array $payload = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ test('ing events can be used to cancel db creation', function () {
|
|||
});
|
||||
|
||||
$tenant = Tenant::create();
|
||||
dispatch_now(new CreateDatabase($tenant));
|
||||
dispatch_sync(new CreateDatabase($tenant));
|
||||
|
||||
pest()->assertFalse($tenant->database()->manager()->databaseExists(
|
||||
$tenant->database()->getName()
|
||||
|
|
@ -171,12 +171,13 @@ test('database is not migrated if creation is disabled', function () {
|
|||
})->toListener()
|
||||
);
|
||||
|
||||
Tenant::create([
|
||||
$tenant = Tenant::create([
|
||||
'tenancy_create_database' => false,
|
||||
'tenancy_db_name' => 'already_created',
|
||||
]);
|
||||
|
||||
expect(pest()->hasFailed())->toBeFalse();
|
||||
// assert test didn't fail
|
||||
$this->assertTrue($tenant->exists());
|
||||
});
|
||||
|
||||
class FooListener extends QueueableListener
|
||||
|
|
|
|||
29
tests/Features/ViteBundlerTest.php
Normal file
29
tests/Features/ViteBundlerTest.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Foundation\Vite;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Stancl\Tenancy\Vite as StanclVite;
|
||||
use Stancl\Tenancy\Features\ViteBundler;
|
||||
|
||||
test('vite helper uses our custom class', function() {
|
||||
$vite = app(Vite::class);
|
||||
|
||||
expect($vite)->toBeInstanceOf(Vite::class);
|
||||
expect($vite)->not()->toBeInstanceOf(StanclVite::class);
|
||||
|
||||
config([
|
||||
'tenancy.features' => [ViteBundler::class],
|
||||
]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
app()->forgetInstance(Vite::class);
|
||||
|
||||
$vite = app(Vite::class);
|
||||
|
||||
expect($vite)->toBeInstanceOf(StanclVite::class);
|
||||
});
|
||||
|
|
@ -302,7 +302,7 @@ test('database credentials can be provided to PermissionControlledMySQLDatabaseM
|
|||
$mysql2DB->statement("CREATE USER `{$username}`@`%` IDENTIFIED BY '{$password}';");
|
||||
$mysql2DB->statement("GRANT ALL PRIVILEGES ON *.* TO `{$username}`@`%` identified by '{$password}' WITH GRANT OPTION;");
|
||||
$mysql2DB->statement("FLUSH PRIVILEGES;");
|
||||
|
||||
|
||||
DB::purge('mysql2'); // forget the mysql2 connection so that it uses the new credentials the next time
|
||||
|
||||
config(['database.connections.mysql2.username' => $username]);
|
||||
|
|
@ -347,7 +347,7 @@ test('tenant database can be created by using the username and password from ten
|
|||
$mysqlDB->statement("CREATE USER `{$username}`@`%` IDENTIFIED BY '{$password}';");
|
||||
$mysqlDB->statement("GRANT ALL PRIVILEGES ON *.* TO `{$username}`@`%` identified by '{$password}' WITH GRANT OPTION;");
|
||||
$mysqlDB->statement("FLUSH PRIVILEGES;");
|
||||
|
||||
|
||||
DB::purge('mysql2'); // forget the mysql2 connection so that it uses the new credentials the next time
|
||||
|
||||
// Remove `mysql` credentials to make sure we will be using the credentials from the tenant config
|
||||
|
|
|
|||
|
|
@ -4,16 +4,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace Stancl\Tenancy\Tests;
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use PDO;
|
||||
use Stancl\Tenancy\Bootstrappers\PrefixCacheTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Facades\GlobalCache;
|
||||
use Dotenv\Dotenv;
|
||||
use Stancl\Tenancy\Facades\Tenancy;
|
||||
use Stancl\Tenancy\TenancyServiceProvider;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Stancl\Tenancy\Bootstrappers\PrefixCacheTenancyBootstrapper;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Stancl\Tenancy\Facades\GlobalCache;
|
||||
use Stancl\Tenancy\TenancyServiceProvider;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\UrlTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\MailTenancyBootstrapper;
|
||||
|
||||
abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||
|
|
@ -105,7 +107,9 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'--force' => true,
|
||||
],
|
||||
'tenancy.bootstrappers.redis' => RedisTenancyBootstrapper::class, // todo1 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'tenancy.bootstrappers.broadcast' => BroadcastTenancyBootstrapper::class, // todo1 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'tenancy.bootstrappers.mail' => MailTenancyBootstrapper::class,
|
||||
'tenancy.bootstrappers.url' => UrlTenancyBootstrapper::class,
|
||||
'queue.connections.central' => [
|
||||
'driver' => 'sync',
|
||||
'central' => true,
|
||||
|
|
@ -116,7 +120,9 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
|
||||
$app->singleton(RedisTenancyBootstrapper::class); // todo (Samuel) use proper approach eg config for singleton registration
|
||||
$app->singleton(PrefixCacheTenancyBootstrapper::class); // todo (Samuel) use proper approach eg config for singleton registration
|
||||
$app->singleton(BroadcastTenancyBootstrapper::class);
|
||||
$app->singleton(MailTenancyBootstrapper::class);
|
||||
$app->singleton(UrlTenancyBootstrapper::class);
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue