mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 05:44:04 +00:00
Merge branch 'master' into configurable-force-rls
This commit is contained in:
commit
f9f9e1814a
92 changed files with 1056 additions and 497 deletions
|
|
@ -11,6 +11,7 @@ use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
|||
use Stancl\Tenancy\Actions\CreateStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Actions\RemoveStorageSymlinksAction;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
|
@ -35,11 +36,15 @@ test('create storage symlinks action works', function() {
|
|||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
$this->assertDirectoryDoesNotExist($publicPath = public_path("public-$tenantKey"));
|
||||
// The symlink doesn't exist
|
||||
expect(is_link($publicPath = public_path("public-$tenantKey")))->toBeFalse();
|
||||
expect(file_exists($publicPath))->toBeFalse();
|
||||
|
||||
(new CreateStorageSymlinksAction)($tenant);
|
||||
|
||||
$this->assertDirectoryExists($publicPath);
|
||||
// The symlink exists and is valid
|
||||
expect(is_link($publicPath = public_path("public-$tenantKey")))->toBeTrue();
|
||||
expect(file_exists($publicPath))->toBeTrue();
|
||||
$this->assertEquals(storage_path("app/public/"), readlink($publicPath));
|
||||
});
|
||||
|
||||
|
|
@ -61,9 +66,48 @@ test('remove storage symlinks action works', function() {
|
|||
|
||||
(new CreateStorageSymlinksAction)($tenant);
|
||||
|
||||
$this->assertDirectoryExists($publicPath = public_path("public-$tenantKey"));
|
||||
// The symlink exists and is valid
|
||||
expect(is_link($publicPath = public_path("public-$tenantKey")))->toBeTrue();
|
||||
expect(file_exists($publicPath))->toBeTrue();
|
||||
|
||||
(new RemoveStorageSymlinksAction)($tenant);
|
||||
|
||||
$this->assertDirectoryDoesNotExist($publicPath);
|
||||
// The symlink doesn't exist
|
||||
expect(is_link($publicPath))->toBeFalse();
|
||||
expect(file_exists($publicPath))->toBeFalse();
|
||||
});
|
||||
|
||||
test('removing tenant symlinks works even if the symlinks are invalid', function() {
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
FilesystemTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||
'tenancy.filesystem.url_override.public' => 'public-%tenant%'
|
||||
]);
|
||||
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
(new CreateStorageSymlinksAction)($tenant);
|
||||
|
||||
// The symlink exists and is valid
|
||||
expect(is_link($publicPath = public_path("public-$tenantKey")))->toBeTrue();
|
||||
expect(file_exists($publicPath))->toBeTrue();
|
||||
|
||||
// Make the symlink invalid by deleting the tenant storage directory
|
||||
$storagePath = storage_path();
|
||||
File::deleteDirectory($storagePath);
|
||||
|
||||
// The symlink still exists, but isn't valid
|
||||
expect(is_link($publicPath))->toBeTrue();
|
||||
expect(file_exists($publicPath))->toBeFalse();
|
||||
|
||||
(new RemoveStorageSymlinksAction)($tenant);
|
||||
|
||||
expect(is_link($publicPath))->toBeFalse();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ use Stancl\Tenancy\Events\TenancyInitialized;
|
|||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
use function Stancl\Tenancy\Tests\withTenantDatabases;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
|||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->mockConsoleOutput = false;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Stancl\Tenancy\Tests\Etc\TestingBroadcaster;
|
|||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastChannelPrefixBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|||
use Stancl\Tenancy\Bootstrappers\CacheTagsBootstrapper;
|
||||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['tenancy.bootstrappers' => [CacheTagsBootstrapper::class]]);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use Stancl\Tenancy\Jobs\CreateDatabase;
|
|||
use Stancl\Tenancy\Listeners;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
/**
|
||||
* This collection of regression tests verifies that SessionTenancyBootstrapper
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ use Stancl\Tenancy\Events\TenancyEnded;
|
|||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|||
use Stancl\Tenancy\Listeners\DeleteTenantStorage;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ use Stancl\Tenancy\Bootstrappers\Integrations\FortifyRouteBootstrapper;
|
|||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
FortifyRouteBootstrapper::$passTenantParameter = true;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
FortifyRouteBootstrapper::$passTenantParameter = true;
|
||||
FortifyRouteBootstrapper::$fortifyRedirectMap = [];
|
||||
FortifyRouteBootstrapper::$fortifyHome = 'tenant.dashboard';
|
||||
FortifyRouteBootstrapper::$defaultParameterNames = false;
|
||||
});
|
||||
|
||||
test('fortify route tenancy bootstrapper updates fortify config correctly', function() {
|
||||
|
|
@ -25,53 +33,31 @@ test('fortify route tenancy bootstrapper updates fortify config correctly', func
|
|||
return true;
|
||||
})->name($homeRouteName = 'home');
|
||||
|
||||
Route::get('/{tenant}/home', function () {
|
||||
return true;
|
||||
})->name($pathIdHomeRouteName = 'tenant.home');
|
||||
|
||||
Route::get('/welcome', function () {
|
||||
return true;
|
||||
})->name($welcomeRouteName = 'welcome');
|
||||
|
||||
Route::get('/{tenant}/welcome', function () {
|
||||
return true;
|
||||
})->name($pathIdWelcomeRouteName = 'path.welcome');
|
||||
|
||||
FortifyRouteBootstrapper::$fortifyHome = $homeRouteName;
|
||||
FortifyRouteBootstrapper::$fortifyRedirectMap['login'] = $welcomeRouteName;
|
||||
|
||||
// Make login redirect to the central welcome route
|
||||
FortifyRouteBootstrapper::$fortifyRedirectMap['login'] = [
|
||||
'route_name' => $welcomeRouteName,
|
||||
'context' => Context::CENTRAL,
|
||||
];
|
||||
expect(config('fortify.home'))->toBe($originalFortifyHome);
|
||||
expect(config('fortify.redirects'))->toBe($originalFortifyRedirects);
|
||||
|
||||
FortifyRouteBootstrapper::$passTenantParameter = true;
|
||||
tenancy()->initialize($tenant = Tenant::create());
|
||||
// The bootstraper makes fortify.home always receive the tenant parameter
|
||||
expect(config('fortify.home'))->toBe('http://localhost/home?tenant=' . $tenant->getTenantKey());
|
||||
|
||||
// The login redirect route has the central context specified, so it doesn't receive the tenant parameter
|
||||
expect(config('fortify.redirects'))->toEqual(['login' => 'http://localhost/welcome']);
|
||||
expect(config('fortify.redirects'))->toEqual(['login' => 'http://localhost/welcome?tenant=' . $tenant->getTenantKey()]);
|
||||
|
||||
tenancy()->end();
|
||||
expect(config('fortify.home'))->toBe($originalFortifyHome);
|
||||
expect(config('fortify.redirects'))->toBe($originalFortifyRedirects);
|
||||
|
||||
// Making a route's context will pass the tenant parameter to the route
|
||||
FortifyRouteBootstrapper::$fortifyRedirectMap['login']['context'] = Context::TENANT;
|
||||
|
||||
FortifyRouteBootstrapper::$passTenantParameter = false;
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
expect(config('fortify.redirects'))->toEqual(['login' => 'http://localhost/welcome?tenant=' . $tenant->getTenantKey()]);
|
||||
|
||||
// Make the home and login route accept the tenant as a route parameter
|
||||
// To confirm that tenant route parameter gets filled automatically too (path identification works as well as query string)
|
||||
FortifyRouteBootstrapper::$fortifyHome = $pathIdHomeRouteName;
|
||||
FortifyRouteBootstrapper::$fortifyRedirectMap['login']['route_name'] = $pathIdWelcomeRouteName;
|
||||
expect(config('fortify.home'))->toBe('http://localhost/home');
|
||||
expect(config('fortify.redirects'))->toEqual(['login' => 'http://localhost/welcome']);
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
expect(config('fortify.home'))->toBe("http://localhost/{$tenant->getTenantKey()}/home");
|
||||
expect(config('fortify.redirects'))->toEqual(['login' => "http://localhost/{$tenant->getTenantKey()}/welcome"]);
|
||||
expect(config('fortify.home'))->toBe($originalFortifyHome);
|
||||
expect(config('fortify.redirects'))->toBe($originalFortifyRedirects);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,18 +10,23 @@ use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\RootUrlBootstrapper;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Bootstrappers\UrlGeneratorBootstrapper;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Overrides\TenancyUrlGenerator;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
RootUrlBootstrapper::$rootUrlOverride = null;
|
||||
RootUrlBootstrapper::$rootUrlOverrideInTests = true;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
RootUrlBootstrapper::$rootUrlOverride = null;
|
||||
RootUrlBootstrapper::$rootUrlOverrideInTests = false;
|
||||
});
|
||||
|
||||
test('root url bootstrapper overrides the root url when tenancy gets initialized and reverts the url to the central one after tenancy ends', function() {
|
||||
test('root url bootstrapper overrides the root url when tenancy gets initialized and reverts the url to the central one when ending tenancy', function() {
|
||||
config(['tenancy.bootstrappers' => [RootUrlBootstrapper::class]]);
|
||||
|
||||
Route::group([
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ beforeEach(function () {
|
|||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
TenancyUrlGenerator::$prefixRouteNames = false;
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = true;
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = false;
|
||||
UrlGeneratorBootstrapper::$addTenantParameterToDefaults = false;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
TenancyUrlGenerator::$prefixRouteNames = false;
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = true;
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = false;
|
||||
UrlGeneratorBootstrapper::$addTenantParameterToDefaults = false;
|
||||
});
|
||||
|
||||
test('url generator bootstrapper swaps the url generator instance correctly', function() {
|
||||
|
|
@ -41,36 +43,28 @@ test('url generator bootstrapper swaps the url generator instance correctly', fu
|
|||
->not()->toBeInstanceOf(TenancyUrlGenerator::class);
|
||||
});
|
||||
|
||||
test('url generator bootstrapper can prefix route names passed to the route helper', function() {
|
||||
test('tenancy url generator can prefix route names passed to the route helper', function() {
|
||||
Route::get('/central/home', fn () => route('home'))->name('home');
|
||||
// Tenant route name prefix is 'tenant.' by default
|
||||
Route::get('/{tenant}/home', fn () => route('tenant.home'))->name('tenant.home')->middleware(['tenant', InitializeTenancyByPath::class]);
|
||||
Route::get('/tenant/home', fn () => route('tenant.home'))->name('tenant.home');
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
$centralRouteUrl = route('home');
|
||||
$tenantRouteUrl = route('tenant.home', ['tenant' => $tenantKey]);
|
||||
TenancyUrlGenerator::$bypassParameter = 'bypassParameter';
|
||||
$tenantRouteUrl = route('tenant.home');
|
||||
|
||||
config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
// Route names don't get prefixed when TenancyUrlGenerator::$prefixRouteNames is false
|
||||
expect(route('home'))->not()->toBe($centralRouteUrl);
|
||||
// When TenancyUrlGenerator::$passTenantParameterToRoutes is true (default)
|
||||
// The route helper receives the tenant parameter
|
||||
// So in order to generate central URL, we have to pass the bypass parameter
|
||||
expect(route('home', ['bypassParameter' => true]))->toBe($centralRouteUrl);
|
||||
|
||||
// Route names don't get prefixed when TenancyUrlGenerator::$prefixRouteNames is false (default)
|
||||
expect(route('home'))->toBe($centralRouteUrl);
|
||||
|
||||
// When $prefixRouteNames is true, the route name passed to the route() helper ('home') gets prefixed with 'tenant.' automatically.
|
||||
TenancyUrlGenerator::$prefixRouteNames = true;
|
||||
// The $prefixRouteNames property is true
|
||||
// The route name passed to the route() helper ('home') gets prefixed prefixed with 'tenant.' automatically
|
||||
|
||||
expect(route('home'))->toBe($tenantRouteUrl);
|
||||
|
||||
// The 'tenant.home' route name doesn't get prefixed because it is already prefixed with 'tenant.'
|
||||
// Also, the route receives the tenant parameter automatically
|
||||
// The 'tenant.home' route name doesn't get prefixed -- it is already prefixed with 'tenant.'
|
||||
expect(route('tenant.home'))->toBe($tenantRouteUrl);
|
||||
|
||||
// Ending tenancy reverts route() behavior changes
|
||||
|
|
@ -79,6 +73,76 @@ test('url generator bootstrapper can prefix route names passed to the route help
|
|||
expect(route('home'))->toBe($centralRouteUrl);
|
||||
});
|
||||
|
||||
test('the route helper can receive the tenant parameter automatically', function (
|
||||
string $identification,
|
||||
bool $addTenantParameterToDefaults,
|
||||
bool $passTenantParameterToRoutes,
|
||||
) {
|
||||
config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]);
|
||||
|
||||
$appUrl = config('app.url');
|
||||
|
||||
UrlGeneratorBootstrapper::$addTenantParameterToDefaults = $addTenantParameterToDefaults;
|
||||
|
||||
// When the tenant parameter isn't added to defaults, the tenant parameter has to be passed "manually"
|
||||
// by setting $passTenantParameterToRoutes to true. This is only preferable with query string identification.
|
||||
// With path identification, this ultimately doesn't have any effect
|
||||
// if UrlGeneratorBootstrapper::$addTenantParameterToDefaults is true,
|
||||
// but TenancyUrlGenerator::$passTenantParameterToRoutes can still be used instead.
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = $passTenantParameterToRoutes;
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
Route::get('/central/home', fn () => route('home'))->name('home');
|
||||
|
||||
$tenantRoute = $identification === InitializeTenancyByPath::class ? "/{tenant}/home" : "/tenant/home";
|
||||
|
||||
Route::get($tenantRoute, fn () => route('tenant.home'))
|
||||
->name('tenant.home')
|
||||
->middleware(['tenant', $identification]);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
$expectedUrl = match (true) {
|
||||
$identification === InitializeTenancyByRequestData::class && $passTenantParameterToRoutes => "{$appUrl}/tenant/home?tenant={$tenantKey}",
|
||||
$identification === InitializeTenancyByRequestData::class => "{$appUrl}/tenant/home", // $passTenantParameterToRoutes is false
|
||||
$identification === InitializeTenancyByPath::class && ($addTenantParameterToDefaults || $passTenantParameterToRoutes) => "{$appUrl}/{$tenantKey}/home",
|
||||
$identification === InitializeTenancyByPath::class => null, // Should throw an exception -- route() doesn't receive the tenant parameter in this case
|
||||
};
|
||||
|
||||
if ($expectedUrl === null) {
|
||||
expect(fn () => route('tenant.home'))->toThrow(UrlGenerationException::class, 'Missing parameter: tenant');
|
||||
} else {
|
||||
expect(route('tenant.home'))->toBe($expectedUrl);
|
||||
}
|
||||
})->with([InitializeTenancyByPath::class, InitializeTenancyByRequestData::class])
|
||||
->with([true, false]) // UrlGeneratorBootstrapper::$addTenantParameterToDefaults
|
||||
->with([true, false]); // TenancyUrlGenerator::$passTenantParameterToRoutes
|
||||
|
||||
test('url generator can override specific route names', function() {
|
||||
config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]);
|
||||
|
||||
Route::get('/foo', fn () => 'foo')->name('foo');
|
||||
Route::get('/bar', fn () => 'bar')->name('bar');
|
||||
Route::get('/baz', fn () => 'baz')->name('baz'); // Not overridden
|
||||
|
||||
TenancyUrlGenerator::$overrides = ['foo' => 'bar'];
|
||||
|
||||
expect(route('foo'))->toBe(url('/foo'));
|
||||
expect(route('bar'))->toBe(url('/bar'));
|
||||
expect(route('baz'))->toBe(url('/baz'));
|
||||
|
||||
tenancy()->initialize(Tenant::create());
|
||||
|
||||
expect(route('foo'))->toBe(url('/bar'));
|
||||
expect(route('bar'))->toBe(url('/bar')); // not overridden
|
||||
expect(route('baz'))->toBe(url('/baz')); // not overridden
|
||||
|
||||
// Bypass the override
|
||||
expect(route('foo', ['central' => true]))->toBe(url('/foo'));
|
||||
});
|
||||
|
||||
test('both the name prefixing and the tenant parameter logic gets skipped when bypass parameter is used', function () {
|
||||
$tenantParameterName = PathTenantResolver::tenantParameterName();
|
||||
|
||||
|
|
@ -105,54 +169,8 @@ test('both the name prefixing and the tenant parameter logic gets skipped when b
|
|||
->not()->toContain('bypassParameter');
|
||||
|
||||
// When the bypass parameter is false, the generated route URL points to the prefixed route ('tenant.home')
|
||||
expect(route('home', ['bypassParameter' => false]))->toBe($tenantRouteUrl)
|
||||
// The tenant parameter is not passed automatically since both
|
||||
// UrlGeneratorBootstrapper::$addTenantParameterToDefaults and TenancyUrlGenerator::$passTenantParameterToRoutes are false by default
|
||||
expect(route('home', ['bypassParameter' => false, 'tenant' => $tenant->getTenantKey()]))->toBe($tenantRouteUrl)
|
||||
->not()->toContain('bypassParameter');
|
||||
});
|
||||
|
||||
test('url generator bootstrapper can make route helper generate links with the tenant parameter', function() {
|
||||
Route::get('/query_string', fn () => route('query_string'))->name('query_string')->middleware(['universal', InitializeTenancyByRequestData::class]);
|
||||
Route::get('/path', fn () => route('path'))->name('path');
|
||||
Route::get('/{tenant}/path', fn () => route('tenant.path'))->name('tenant.path')->middleware([InitializeTenancyByPath::class]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
$queryStringCentralUrl = route('query_string');
|
||||
$queryStringTenantUrl = route('query_string', ['tenant' => $tenantKey]);
|
||||
$pathCentralUrl = route('path');
|
||||
$pathTenantUrl = route('tenant.path', ['tenant' => $tenantKey]);
|
||||
|
||||
// Makes the route helper receive the tenant parameter whenever available
|
||||
// Unless the bypass parameter is true
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = true;
|
||||
|
||||
TenancyUrlGenerator::$bypassParameter = 'bypassParameter';
|
||||
|
||||
config(['tenancy.bootstrappers' => [UrlGeneratorBootstrapper::class]]);
|
||||
|
||||
expect(route('path'))->toBe($pathCentralUrl);
|
||||
// Tenant parameter required, but not passed since tenancy wasn't initialized
|
||||
expect(fn () => route('tenant.path'))->toThrow(UrlGenerationException::class);
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
// Tenant parameter is passed automatically
|
||||
expect(route('path'))->not()->toBe($pathCentralUrl); // Parameter added as query string – bypassParameter needed
|
||||
expect(route('path', ['bypassParameter' => true]))->toBe($pathCentralUrl);
|
||||
expect(route('tenant.path'))->toBe($pathTenantUrl);
|
||||
|
||||
expect(route('query_string'))->toBe($queryStringTenantUrl)->toContain('tenant=');
|
||||
expect(route('query_string', ['bypassParameter' => 'true']))->toBe($queryStringCentralUrl)->not()->toContain('tenant=');
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
expect(route('query_string'))->toBe($queryStringCentralUrl);
|
||||
|
||||
// Tenant parameter required, but shouldn't be passed since tenancy isn't initialized
|
||||
expect(fn () => route('tenant.path'))->toThrow(UrlGenerationException::class);
|
||||
|
||||
// Route-level identification
|
||||
pest()->get("http://localhost/query_string")->assertSee($queryStringCentralUrl);
|
||||
pest()->get("http://localhost/query_string?tenant=$tenantKey")->assertSee($queryStringTenantUrl);
|
||||
pest()->get("http://localhost/path")->assertSee($pathCentralUrl);
|
||||
pest()->get("http://localhost/$tenantKey/path")->assertSee($pathTenantUrl);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
|
|||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastingConfigBootstrapper;
|
||||
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
|
||||
use function Stancl\Tenancy\Tests\withTenantDatabases;
|
||||
|
||||
beforeEach(function () {
|
||||
withTenantDatabases();
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ use Stancl\Tenancy\Resolvers\PathTenantResolver;
|
|||
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
|
||||
use Illuminate\Support\Facades\Route as RouteFacade;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException;
|
||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\PathIdentificationManager;
|
||||
use Stancl\Tenancy\Resolvers\RequestDataTenantResolver;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('tenants can be resolved using cached resolvers', function (string $resolver) {
|
||||
$tenant = Tenant::create(['id' => $tenantKey = 'acme']);
|
||||
|
|
@ -84,6 +86,34 @@ test('cache is invalidated when the tenant is updated', function (string $resolv
|
|||
RequestDataTenantResolver::class,
|
||||
]);
|
||||
|
||||
test('cache is invalidated when the tenant is deleted', function (string $resolver) {
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0;'); // allow deleting the tenant
|
||||
$tenant = Tenant::create(['id' => $tenantKey = 'acme']);
|
||||
$tenant->createDomain($tenantKey);
|
||||
|
||||
DB::enableQueryLog();
|
||||
|
||||
config(['tenancy.identification.resolvers.' . $resolver . '.cache' => true]);
|
||||
|
||||
expect($tenant->is(app($resolver)->resolve(getResolverArgument($resolver, $tenantKey))))->toBeTrue();
|
||||
expect(DB::getQueryLog())->not()->toBeEmpty();
|
||||
|
||||
DB::flushQueryLog();
|
||||
|
||||
expect($tenant->is(app($resolver)->resolve(getResolverArgument($resolver, $tenantKey))))->toBeTrue();
|
||||
expect(DB::getQueryLog())->toBeEmpty();
|
||||
|
||||
$tenant->delete();
|
||||
DB::flushQueryLog();
|
||||
|
||||
expect(fn () => app($resolver)->resolve(getResolverArgument($resolver, $tenantKey)))->toThrow(TenantCouldNotBeIdentifiedException::class);
|
||||
expect(DB::getQueryLog())->not()->toBeEmpty(); // Cache was invalidated, so the DB was queried
|
||||
})->with([
|
||||
DomainTenantResolver::class,
|
||||
PathTenantResolver::class,
|
||||
RequestDataTenantResolver::class,
|
||||
]);
|
||||
|
||||
test('cache is invalidated when a tenants domain is changed', function () {
|
||||
$tenant = Tenant::create(['id' => $tenantKey = 'acme']);
|
||||
$tenant->createDomain($tenantKey);
|
||||
|
|
@ -110,6 +140,26 @@ test('cache is invalidated when a tenants domain is changed', function () {
|
|||
pest()->assertNotEmpty(DB::getQueryLog()); // not empty
|
||||
});
|
||||
|
||||
test('cache is invalidated when a tenants domain is deleted', function () {
|
||||
$tenant = Tenant::create(['id' => $tenantKey = 'acme']);
|
||||
$tenant->createDomain($tenantKey);
|
||||
|
||||
DB::enableQueryLog();
|
||||
|
||||
config(['tenancy.identification.resolvers.' . DomainTenantResolver::class . '.cache' => true]);
|
||||
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
DB::flushQueryLog();
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
expect(DB::getQueryLog())->toBeEmpty(); // empty
|
||||
|
||||
$tenant->domains->first()->delete();
|
||||
DB::flushQueryLog();
|
||||
|
||||
expect(fn () => $tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toThrow(TenantCouldNotBeIdentifiedOnDomainException::class);
|
||||
expect(DB::getQueryLog())->not()->toBeEmpty(); // Cache was invalidated, so the DB was queried
|
||||
});
|
||||
|
||||
test('PathTenantResolver forgets the tenant route parameter when the tenant is resolved from cache', function() {
|
||||
config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.cache' => true]);
|
||||
DB::enableQueryLog();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Route as RouteFacade;
|
|||
use Stancl\Tenancy\Tests\Etc\HasMiddlewareController;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('a route can be universal using path identification', function (array $routeMiddleware, array $globalMiddleware) {
|
||||
foreach ($globalMiddleware as $middleware) {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Stancl\Tenancy\Database\Concerns\HasDomains;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
|
||||
use Stancl\Tenancy\Database\Models;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Route::group([
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
|||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Database\Exceptions\TenantDatabaseDoesNotExistException;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
if (file_exists($schemaPath = 'tests/Etc/tenant-schema-test.dump')) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledMySQLData
|
|||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLSchemaManager;
|
||||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLDatabaseManager;
|
||||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledMicrosoftSQLServerDatabaseManager;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config([
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use Stancl\Tenancy\Exceptions\DomainOccupiedByOtherTenantException;
|
|||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
InitializeTenancyByDomain::$onFail = null;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByOriginHeader;
|
|||
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithMiddleware;
|
||||
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithRouteMiddleware;
|
||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config()->set([
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use Stancl\Tenancy\Events\BootstrappingTenancy;
|
|||
use Stancl\Tenancy\Listeners\QueueableListener;
|
||||
use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
FooListener::$shouldQueue = false;
|
||||
|
|
|
|||
|
|
@ -18,14 +18,9 @@ use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('sqlite ATTACH statements can be blocked', function (bool $disallow) {
|
||||
try {
|
||||
readlink(base_path('vendor'));
|
||||
} catch (\Throwable) {
|
||||
symlink(base_path('vendor'), '/var/www/html/vendor');
|
||||
}
|
||||
|
||||
if (php_uname('m') == 'aarch64') {
|
||||
// Escape testbench prison. Can't hardcode /var/www/html/extensions/... here
|
||||
// since GHA doesn't mount the filesystem on the container's workdir
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Stancl\Tenancy\Features\CrossDomainRedirect;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('tenant redirect macro replaces only the hostname', function () {
|
||||
config([
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Stancl\Tenancy\Features\TenantConfig;
|
|||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
afterEach(function () {
|
||||
TenantConfig::$storageToConfigMap = [];
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ use Stancl\Tenancy\Events\TenancyInitialized;
|
|||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\MailConfigBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
use function Stancl\Tenancy\Tests\withTenantDatabases;
|
||||
|
||||
beforeEach(function() {
|
||||
config(['mail.default' => 'smtp']);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Stancl\Tenancy\Database\Concerns\MaintenanceMode;
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Stancl\Tenancy\Middleware\CheckTenantForMaintenanceMode;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
beforeEach(function () {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ use Stancl\Tenancy\Jobs\CreateDatabase;
|
|||
use Stancl\Tenancy\Listeners\CreateTenantConnection;
|
||||
use Stancl\Tenancy\Listeners\UseCentralConnection;
|
||||
use Stancl\Tenancy\Listeners\UseTenantConnection;
|
||||
use \Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('manual tenancy initialization works', function () {
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByOriginHeader;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
InitializeTenancyByOriginHeader::$onFail = null;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByPathException;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Resolvers\PathTenantResolver;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
// Make sure the tenant parameter is set to 'tenant'
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Stancl\Tenancy\Events\PendingTenantCreated;
|
|||
use Stancl\Tenancy\Events\PendingTenantPulled;
|
||||
use Stancl\Tenancy\Events\PullingPendingTenant;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('tenants are correctly identified as pending', function (){
|
||||
Tenant::createPending();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Tests;
|
||||
|
||||
use Stancl\Tenancy\Tests\TestCase;
|
||||
use Stancl\JobPipeline\JobPipeline;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
|
@ -8,14 +10,14 @@ use Stancl\Tenancy\Events\TenantCreated;
|
|||
|
||||
uses(TestCase::class)->in(__DIR__);
|
||||
|
||||
function pest(): TestCase
|
||||
{
|
||||
return Pest\TestSuite::getInstance()->test;
|
||||
}
|
||||
|
||||
function withTenantDatabases()
|
||||
{
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener());
|
||||
}
|
||||
|
||||
function pest(): TestCase
|
||||
{
|
||||
return \Pest\TestSuite::getInstance()->test;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
|||
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
|
||||
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithMiddleware;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('correct routes are accessible in route-level identification', function (RouteMode $defaultRouteMode) {
|
||||
config()->set([
|
||||
|
|
|
|||
|
|
@ -22,16 +22,14 @@ use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\PersistentQueueTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Listeners\QueueableListener;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
use function Stancl\Tenancy\Tests\withTenantDatabases;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->mockConsoleOutput = false;
|
||||
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
QueueTenancyBootstrapper::class,
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class],
|
||||
'queue.default' => 'redis',
|
||||
]);
|
||||
|
||||
|
|
@ -45,7 +43,22 @@ afterEach(function () {
|
|||
pest()->valuestore->flush();
|
||||
});
|
||||
|
||||
test('tenant id is passed to tenant queues', function () {
|
||||
dataset('queue_bootstrappers', [
|
||||
QueueTenancyBootstrapper::class,
|
||||
PersistentQueueTenancyBootstrapper::class,
|
||||
]);
|
||||
|
||||
function withQueueBootstrapper(string $class) {
|
||||
config(['tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
$class,
|
||||
]]);
|
||||
|
||||
$class::__constructStatic(app());
|
||||
}
|
||||
|
||||
test('tenant id is passed to tenant queues', function (string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withTenantDatabases();
|
||||
|
||||
config(['queue.default' => 'sync']);
|
||||
|
|
@ -61,9 +74,10 @@ test('tenant id is passed to tenant queues', function () {
|
|||
Event::assertDispatched(JobProcessing::class, function ($event) {
|
||||
return $event->job->payload()['tenant_id'] === tenant('id');
|
||||
});
|
||||
});
|
||||
})->with('queue_bootstrappers');
|
||||
|
||||
test('tenant id is not passed to central queues', function () {
|
||||
test('tenant id is not passed to central queues', function (string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withTenantDatabases();
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
|
@ -82,9 +96,10 @@ test('tenant id is not passed to central queues', function () {
|
|||
Event::assertDispatched(JobProcessing::class, function ($event) {
|
||||
return ! isset($event->job->payload()['tenant_id']);
|
||||
});
|
||||
});
|
||||
})->with('queue_bootstrappers');
|
||||
|
||||
test('tenancy is initialized inside queues', function (bool $shouldEndTenancy) {
|
||||
test('tenancy is initialized inside queues', function (bool $shouldEndTenancy, string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withTenantDatabases();
|
||||
withFailedJobs();
|
||||
|
||||
|
|
@ -117,7 +132,7 @@ test('tenancy is initialized inside queues', function (bool $shouldEndTenancy) {
|
|||
$tenant->run(function () use ($user) {
|
||||
expect($user->fresh()->name)->toBe('Bar');
|
||||
});
|
||||
})->with([true, false]);
|
||||
})->with([true, false])->with('queue_bootstrappers');
|
||||
|
||||
test('changing the shouldQueue static property in parent class affects child classes unless the property is redefined', function () {
|
||||
// Parent – $shouldQueue is true
|
||||
|
|
@ -142,7 +157,8 @@ test('changing the shouldQueue static property in parent class affects child cla
|
|||
expect(app(ShouldNotQueueListener::class)->shouldQueue(new stdClass()))->toBeFalse();
|
||||
});
|
||||
|
||||
test('tenancy is initialized when retrying jobs', function (bool $shouldEndTenancy) {
|
||||
test('tenancy is initialized when retrying jobs', function (bool $shouldEndTenancy, string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withFailedJobs();
|
||||
withTenantDatabases();
|
||||
|
||||
|
|
@ -189,9 +205,10 @@ test('tenancy is initialized when retrying jobs', function (bool $shouldEndTenan
|
|||
$tenant->run(function () use ($user) {
|
||||
expect($user->fresh()->name)->toBe('Bar');
|
||||
});
|
||||
})->with([true, false]);
|
||||
})->with([true, false])->with('queue_bootstrappers');
|
||||
|
||||
test('the tenant used by the job doesnt change when the current tenant changes', function () {
|
||||
test('the tenant used by the job doesnt change when the current tenant changes', function (string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withTenantDatabases();
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
|
|
@ -208,26 +225,11 @@ test('the tenant used by the job doesnt change when the current tenant changes',
|
|||
pest()->artisan('queue:work --once');
|
||||
|
||||
expect(pest()->valuestore->get('tenant_id'))->toBe('The current tenant id is: ' . $tenant1->getTenantKey());
|
||||
});
|
||||
|
||||
test('tenant connections do not persist after tenant jobs get processed', function() {
|
||||
withTenantDatabases();
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
||||
tenancy()->initialize($tenant);
|
||||
|
||||
dispatch(new TestJob(pest()->valuestore));
|
||||
|
||||
tenancy()->end();
|
||||
|
||||
pest()->artisan('queue:work --once');
|
||||
|
||||
expect(collect(DB::select('SHOW FULL PROCESSLIST'))->pluck('db'))->not()->toContain($tenant->database()->getName());
|
||||
});
|
||||
})->with('queue_bootstrappers');
|
||||
|
||||
// Regression test for #1277
|
||||
test('dispatching a job from a tenant run arrow function dispatches it immediately', function () {
|
||||
test('dispatching a job from a tenant run arrow function dispatches it immediately', function (string $bootstrapper) {
|
||||
withQueueBootstrapper($bootstrapper);
|
||||
withTenantDatabases();
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
|
@ -241,7 +243,7 @@ test('dispatching a job from a tenant run arrow function dispatches it immediate
|
|||
expect(tenant())->toBe(null);
|
||||
|
||||
expect(pest()->valuestore->get('tenant_id'))->toBe('The current tenant id is: ' . $tenant->getTenantKey());
|
||||
});
|
||||
})->with('queue_bootstrappers');
|
||||
|
||||
function createValueStore(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use Stancl\Tenancy\Commands\CreateUserWithRLSPolicies;
|
|||
use Stancl\Tenancy\RLS\PolicyManagers\TableRLSManager;
|
||||
use Stancl\Tenancy\RLS\PolicyManagers\TraitRLSManager;
|
||||
use Stancl\Tenancy\Bootstrappers\PostgresRLSBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
CreateUserWithRLSPolicies::$forceRls = true;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use Stancl\Tenancy\Commands\CreateUserWithRLSPolicies;
|
|||
use Stancl\Tenancy\RLS\PolicyManagers\TableRLSManager;
|
||||
use Stancl\Tenancy\Bootstrappers\PostgresRLSBootstrapper;
|
||||
use Stancl\Tenancy\Database\Exceptions\RecursiveRelationshipException;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
CreateUserWithRLSPolicies::$forceRls = true;
|
||||
|
|
@ -509,7 +510,7 @@ test('table rls manager generates relationship trees with tables related to the
|
|||
|
||||
// Add non-nullable comment_id foreign key
|
||||
Schema::table('ratings', function (Blueprint $table) {
|
||||
$table->foreignId('comment_id')->onUpdate('cascade')->onDelete('cascade')->comment('rls')->constrained('comments');
|
||||
$table->foreignId('comment_id')->comment('rls')->constrained('comments')->onUpdate('cascade')->onDelete('cascade');
|
||||
});
|
||||
|
||||
// Non-nullable paths are preferred over nullable paths
|
||||
|
|
@ -744,16 +745,29 @@ test('table rls manager generates queries correctly', function() {
|
|||
test('table manager throws an exception when encountering a recursive relationship', function() {
|
||||
Schema::create('recursive_posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('highlighted_comment_id')->constrained('comments')->nullable()->comment('rls');
|
||||
$table->foreignId('highlighted_comment_id')->nullable()->comment('rls')->constrained('comments');
|
||||
});
|
||||
|
||||
Schema::table('comments', function (Blueprint $table) {
|
||||
$table->foreignId('recursive_post_id')->constrained('recursive_posts')->comment('rls');
|
||||
$table->foreignId('recursive_post_id')->comment('rls')->constrained('recursive_posts');
|
||||
});
|
||||
|
||||
expect(fn () => app(TableRLSManager::class)->generateTrees())->toThrow(RecursiveRelationshipException::class);
|
||||
});
|
||||
|
||||
test('table manager ignores recursive relationship if the foreign key responsible for the recursion has no-rls comment', function() {
|
||||
Schema::create('recursive_posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('highlighted_comment_id')->nullable()->comment('no-rls')->constrained('comments');
|
||||
});
|
||||
|
||||
Schema::table('comments', function (Blueprint $table) {
|
||||
$table->foreignId('recursive_post_id')->comment('rls')->constrained('recursive_posts');
|
||||
});
|
||||
|
||||
expect(fn () => app(TableRLSManager::class)->generateTrees())->not()->toThrow(RecursiveRelationshipException::class);
|
||||
});
|
||||
|
||||
function createPostgresUser(string $username, string $password = 'password'): array
|
||||
{
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Stancl\Tenancy\Commands\CreateUserWithRLSPolicies;
|
|||
use Stancl\Tenancy\RLS\PolicyManagers\TraitRLSManager;
|
||||
use Stancl\Tenancy\Bootstrappers\PostgresRLSBootstrapper;
|
||||
use Stancl\Tenancy\Database\Concerns\BelongsToPrimaryModel;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
CreateUserWithRLSPolicies::$forceRls = true;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Route;
|
|||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByRequestDataException;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config([
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ use Stancl\Tenancy\ResourceSyncing\Events\CentralResourceDetachedFromTenant;
|
|||
use Stancl\Tenancy\Tests\Etc\ResourceSyncing\CentralUser as BaseCentralUser;
|
||||
use Stancl\Tenancy\ResourceSyncing\CentralResourceNotAvailableInPivotException;
|
||||
use Stancl\Tenancy\ResourceSyncing\Events\SyncedResourceSavedInForeignDatabase;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['tenancy.bootstrappers' => [
|
||||
|
|
|
|||
73
tests/RunForMultipleTest.php
Normal file
73
tests/RunForMultipleTest.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Stancl\Tenancy\Events\TenantCreated;
|
||||
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||
use Stancl\Tenancy\Jobs\MigrateDatabase;
|
||||
use Stancl\JobPipeline\JobPipeline;
|
||||
use Stancl\Tenancy\Tests\Etc\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
|
||||
beforeEach(function () {
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([
|
||||
CreateDatabase::class,
|
||||
MigrateDatabase::class,
|
||||
])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener());
|
||||
|
||||
config(['tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
]]);
|
||||
});
|
||||
|
||||
test('runForMultiple runs the passed closure for the right tenants', function() {
|
||||
$tenants = [Tenant::create(), Tenant::create(), Tenant::create()];
|
||||
|
||||
$createUser = fn ($username) => function () use ($username) {
|
||||
User::create(['name' => $username, 'email' => Str::random(8) . '@example.com', 'password' => bcrypt('password')]);
|
||||
};
|
||||
|
||||
// tenancy()->runForMultiple([], ...) shouldn't do anything
|
||||
// No users should be created -- the closure should not run at all
|
||||
tenancy()->runForMultiple([], $createUser('none'));
|
||||
// Try the same with an empty collection -- the result should be the same for any traversable
|
||||
tenancy()->runForMultiple(collect(), $createUser('none'));
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
$tenant->run(function() {
|
||||
expect(User::count())->toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
// tenancy()->runForMultiple(['foo', 'bar'], ...) should run the closure only for the passed tenants
|
||||
tenancy()->runForMultiple([$tenants[0]->getTenantKey(), $tenants[1]->getTenantKey()], $createUser('user'));
|
||||
|
||||
// User should be created for tenants[0] and tenants[1], but not for tenants[2]
|
||||
foreach ($tenants as $tenant) {
|
||||
$tenant->run(function() use ($tenants) {
|
||||
if (tenant()->getTenantKey() !== $tenants[2]->getTenantKey()) {
|
||||
expect(User::first()->name)->toBe('user');
|
||||
} else {
|
||||
expect(User::count())->toBe(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// tenancy()->runForMultiple(null, ...) should run the closure for all tenants
|
||||
tenancy()->runForMultiple(null, $createUser('new_user'));
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
$tenant->run(function() {
|
||||
expect(User::all()->pluck('name'))->toContain('new_user');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -8,6 +8,7 @@ use Stancl\Tenancy\Exceptions\TenancyNotInitializedException;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Middleware\ScopeSessions;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Route::group([
|
||||
|
|
@ -54,3 +55,15 @@ test('an exception is thrown when the middleware is executed before tenancy is i
|
|||
pest()->expectException(TenancyNotInitializedException::class);
|
||||
$this->withoutExceptionHandling()->get('http://acme.localhost/bar');
|
||||
});
|
||||
|
||||
test('scope sessions mw can be used on universal routes', function() {
|
||||
Route::get('/universal', function () {
|
||||
return true;
|
||||
})->middleware(['universal', InitializeTenancyBySubdomain::class, ScopeSessions::class]);
|
||||
|
||||
Tenant::create([
|
||||
'id' => 'acme',
|
||||
])->createDomain('acme');
|
||||
|
||||
pest()->withoutExceptionHandling()->get('http://localhost/universal')->assertSuccessful();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
// todo@tests write similar low-level tests for the cache bootstrapper? including the database driver in a single-db setup
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
|
||||
use Stancl\Tenancy\Database\Concerns\BelongsToPrimaryModel;
|
||||
use Stancl\Tenancy\Database\Concerns\HasScopedValidationRules;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
Schema::create('posts', function (Blueprint $table) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use Illuminate\Database\UniqueConstraintViolationException;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
|
||||
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['tenancy.models.tenant' => SingleDomainTenant::class]);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Stancl\Tenancy\Database\Concerns\HasDomains;
|
|||
use Stancl\Tenancy\Exceptions\NotASubdomainException;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Database\Models;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
// Global state cleanup after some tests
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use Stancl\Tenancy\Controllers\TenantAssetController;
|
|||
use Stancl\Tenancy\Events\TenancyEnded;
|
||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||
use Stancl\Tenancy\Overrides\TenancyUrlGenerator;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['tenancy.bootstrappers' => [
|
||||
|
|
@ -65,7 +66,7 @@ test('asset can be accessed using the url returned by the tenant asset helper',
|
|||
|
||||
test('asset helper returns a link to tenant asset controller when asset url is null', function () {
|
||||
config(['app.asset_url' => null]);
|
||||
config(['tenancy.filesystem.asset_helper_tenancy' => true]);
|
||||
config(['tenancy.filesystem.asset_helper_override' => true]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
tenancy()->initialize($tenant);
|
||||
|
|
@ -78,7 +79,7 @@ test('asset helper returns a link to tenant asset controller when asset url is n
|
|||
|
||||
test('asset helper returns a link to an external url when asset url is not null', function () {
|
||||
config(['app.asset_url' => 'https://an-s3-bucket']);
|
||||
config(['tenancy.filesystem.asset_helper_tenancy' => true]);
|
||||
config(['tenancy.filesystem.asset_helper_override' => true]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
tenancy()->initialize($tenant);
|
||||
|
|
@ -93,7 +94,7 @@ test('asset helper works correctly with path identification', function (bool $ke
|
|||
TenancyUrlGenerator::$prefixRouteNames = true;
|
||||
TenancyUrlGenerator::$passTenantParameterToRoutes = true;
|
||||
|
||||
config(['tenancy.filesystem.asset_helper_tenancy' => true]);
|
||||
config(['tenancy.filesystem.asset_helper_override' => true]);
|
||||
config(['tenancy.identification.default_middleware' => InitializeTenancyByPath::class]);
|
||||
config(['tenancy.bootstrappers' => array_merge([UrlGeneratorBootstrapper::class], config('tenancy.bootstrappers'))]);
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ test('asset helper tenancy can be disabled', function () {
|
|||
|
||||
config([
|
||||
'app.asset_url' => null,
|
||||
'tenancy.filesystem.asset_helper_tenancy' => false,
|
||||
'tenancy.filesystem.asset_helper_override' => false,
|
||||
]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('commands run globally are tenant aware and return valid exit code', function () {
|
||||
$tenant1 = Tenant::create();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledMySQLData
|
|||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLSchemaManager;
|
||||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLDatabaseManager;
|
||||
use Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledMicrosoftSQLServerDatabaseManager;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
SQLiteDatabaseManager::$path = null;
|
||||
|
|
@ -405,6 +406,42 @@ test('tenant database can be created by using the username and password from ten
|
|||
expect($manager->databaseExists($name))->toBeTrue();
|
||||
});
|
||||
|
||||
test('decrypted password can be used to connect to a tenant db while the password is saved as encrypted', function (string|null $tenantDbPassword) {
|
||||
config([
|
||||
'tenancy.database.managers.mysql' => PermissionControlledMySQLDatabaseManager::class,
|
||||
'tenancy.database.template_tenant_connection' => 'mysql',
|
||||
]);
|
||||
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener());
|
||||
|
||||
// Create a tenant, either with a specific password, or with a password generated by the DB manager
|
||||
$tenant = TenantWithEncryptedPassword::create([
|
||||
'tenancy_db_name' => $name = 'foo' . Str::random(8),
|
||||
'tenancy_db_username' => 'user' . Str::random(4),
|
||||
'tenancy_db_password' => $tenantDbPassword,
|
||||
]);
|
||||
|
||||
$decryptedPassword = $tenant->tenancy_db_password;
|
||||
$encryptedPassword = $tenant->getAttributes()['tenancy_db_password']; // Password encrypted using the TenantWithEncryptedPassword model's encrypted cast
|
||||
expect($decryptedPassword)->not()->toBe($encryptedPassword);
|
||||
|
||||
$passwordSavedInDatabase = json_decode(DB::select('SELECT data FROM tenants LIMIT 1')[0]->data)->tenancy_db_password;
|
||||
expect($encryptedPassword)->toBe($passwordSavedInDatabase);
|
||||
|
||||
app(DatabaseManager::class)->connectToTenant($tenant);
|
||||
|
||||
// Check if we got connected to the tenant DB
|
||||
expect(config('database.default'))->toBe('tenant');
|
||||
expect(config('database.connections.tenant.database'))->toBe($name);
|
||||
// Check if the decrypted password is used to connect to the tenant DB
|
||||
expect(config('database.connections.tenant.password'))->toBe($decryptedPassword);
|
||||
})->with([
|
||||
'decrypted' . Str::random(8), // Use this password as the tenant DB password
|
||||
null, // Let the DB manager generate the tenant DB password
|
||||
]);
|
||||
|
||||
test('path used by sqlite manager can be customized', function () {
|
||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
|
|
@ -529,3 +566,13 @@ function createUsersTable()
|
|||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
class TenantWithEncryptedPassword extends Tenant
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'tenancy_db_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,16 @@ use Stancl\Tenancy\Tests\Etc\Tenant;
|
|||
use Stancl\Tenancy\UniqueIdentifierGenerators\UUIDGenerator;
|
||||
use Stancl\Tenancy\Exceptions\TenancyNotInitializedException;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\RandomHexGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\RandomIntGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\RandomStringGenerator;
|
||||
use Stancl\Tenancy\UniqueIdentifierGenerators\ULIDGenerator;
|
||||
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
afterEach(function () {
|
||||
RandomIntGenerator::$min = 0;
|
||||
RandomIntGenerator::$max = PHP_INT_MAX;
|
||||
});
|
||||
|
||||
test('created event is dispatched', function () {
|
||||
Event::fake([TenantCreated::class]);
|
||||
|
|
@ -71,6 +80,20 @@ test('autoincrement ids are supported', function () {
|
|||
expect($tenant2->id)->toBe(2);
|
||||
});
|
||||
|
||||
test('ulid ids are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, ULIDGenerator::class);
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
expect($tenant1->id)->toBeString();
|
||||
expect(strlen($tenant1->id))->toBe(26);
|
||||
|
||||
$tenant2 = Tenant::create();
|
||||
expect($tenant2->id)->toBeString();
|
||||
expect(strlen($tenant2->id))->toBe(26);
|
||||
|
||||
expect($tenant2->id > $tenant1->id)->toBeTrue();
|
||||
});
|
||||
|
||||
test('hex ids are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, RandomHexGenerator::class);
|
||||
|
||||
|
|
@ -87,6 +110,16 @@ test('hex ids are supported', function () {
|
|||
RandomHexGenerator::$bytes = 6; // reset
|
||||
});
|
||||
|
||||
test('random ints are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, RandomIntGenerator::class);
|
||||
RandomIntGenerator::$min = 200;
|
||||
RandomIntGenerator::$max = 1000;
|
||||
|
||||
$tenant1 = Tenant::create();
|
||||
expect($tenant1->id >= 200)->toBeTrue();
|
||||
expect($tenant1->id <= 1000)->toBeTrue();
|
||||
});
|
||||
|
||||
test('random string ids are supported', function () {
|
||||
app()->bind(UniqueIdentifierGenerator::class, RandomStringGenerator::class);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Exceptions\StatefulGuardRequiredException;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
beforeEach(function () {
|
||||
pest()->artisan('migrate', [
|
||||
|
|
@ -88,7 +89,7 @@ test('tenant user can be impersonated on a tenant domain', function () {
|
|||
expect(session('tenancy_impersonating'))->toBeTrue();
|
||||
|
||||
// Leave impersonation
|
||||
UserImpersonation::leave();
|
||||
UserImpersonation::stopImpersonating();
|
||||
|
||||
expect(UserImpersonation::isImpersonating())->toBeFalse();
|
||||
expect(session('tenancy_impersonating'))->toBeNull();
|
||||
|
|
@ -134,7 +135,7 @@ test('tenant user can be impersonated on a tenant path', function () {
|
|||
expect(session('tenancy_impersonating'))->toBeTrue();
|
||||
|
||||
// Leave impersonation
|
||||
UserImpersonation::leave();
|
||||
UserImpersonation::stopImpersonating();
|
||||
|
||||
expect(UserImpersonation::isImpersonating())->toBeFalse();
|
||||
expect(session('tenancy_impersonating'))->toBeNull();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ use Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper;
|
|||
use Stancl\Tenancy\Bootstrappers\UrlGeneratorBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastingConfigBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\BroadcastChannelPrefixBootstrapper;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||
{
|
||||
|
|
@ -85,11 +87,8 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'--realpath' => true,
|
||||
]);
|
||||
|
||||
// Laravel 6.x support todo@refactor clean up
|
||||
$testResponse = class_exists('Illuminate\Testing\TestResponse') ? 'Illuminate\Testing\TestResponse' : 'Illuminate\Foundation\Testing\TestResponse';
|
||||
$testResponse::macro('assertContent', function ($content) {
|
||||
$assertClass = class_exists('Illuminate\Testing\Assert') ? 'Illuminate\Testing\Assert' : 'Illuminate\Foundation\Testing\Assert';
|
||||
$assertClass::assertSame($content, $this->baseResponse->getContent());
|
||||
\Illuminate\Testing\TestResponse::macro('assertContent', function ($content) {
|
||||
\Illuminate\Testing\Assert::assertSame($content, $this->baseResponse->getContent());
|
||||
|
||||
return $this;
|
||||
});
|
||||
|
|
@ -175,18 +174,25 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'tenancy.models.tenant' => Tenant::class, // Use test tenant w/ DBs & domains
|
||||
]);
|
||||
|
||||
$app->singleton(RedisTenancyBootstrapper::class); // todo@samuel use proper approach eg config for singleton registration
|
||||
$app->singleton(CacheTenancyBootstrapper::class); // todo@samuel use proper approach eg config for singleton registration
|
||||
// Since we run the TSP with no bootstrappers enabled, we need
|
||||
// to manually register bootstrappers as singletons here.
|
||||
$app->singleton(RedisTenancyBootstrapper::class);
|
||||
$app->singleton(CacheTenancyBootstrapper::class);
|
||||
$app->singleton(BroadcastingConfigBootstrapper::class);
|
||||
$app->singleton(BroadcastChannelPrefixBootstrapper::class);
|
||||
$app->singleton(PostgresRLSBootstrapper::class);
|
||||
$app->singleton(MailConfigBootstrapper::class);
|
||||
$app->singleton(RootUrlBootstrapper::class);
|
||||
$app->singleton(UrlGeneratorBootstrapper::class);
|
||||
$app->singleton(FilesystemTenancyBootstrapper::class);
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app)
|
||||
{
|
||||
TenancyServiceProvider::$configure = function () {
|
||||
config(['tenancy.bootstrappers' => []]);
|
||||
};
|
||||
|
||||
return [
|
||||
TenancyServiceProvider::class,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
use Illuminate\Http\Request;
|
||||
use Stancl\Tenancy\Enums\RouteMode;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
|
|
@ -11,16 +9,14 @@ use Stancl\Tenancy\Resolvers\PathTenantResolver;
|
|||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Route as RouteFacade;
|
||||
use Stancl\Tenancy\Tests\Etc\HasMiddlewareController;
|
||||
use Stancl\Tenancy\Middleware\IdentificationMiddleware;
|
||||
use Stancl\Tenancy\Resolvers\RequestDataTenantResolver;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Concerns\UsableWithEarlyIdentification;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
|
||||
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
|
||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
|
||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByRequestDataException;
|
||||
use function Stancl\Tenancy\Tests\pest;
|
||||
|
||||
test('a route can be universal using domain identification', function (array $routeMiddleware, array $globalMiddleware) {
|
||||
foreach ($globalMiddleware as $middleware) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue