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

Improve early identification tests (#66)

* Separate route-level domain identification test from path/request to improve readability WIP

* Get rid of confusing datasets in route-level identifcation tests

* Clean up updated tests

* Simplify early id tests

* Reduce dataset duplication

* Improve test readability, fix false positive,  polish details

* Separate early ID test from defaulting test (WIP)

* Finish improving and correcting the early identification/default route mode tests

* Make flag/default mode usage more clear by improving the docblock in DealsWithRouteContexts

* Fix PHPUnit deprecation warnings

* code review

* code review

---------

Co-authored-by: Samuel Štancl <samuel@archte.ch>
This commit is contained in:
lukinovec 2024-12-20 03:42:23 +01:00 committed by GitHub
parent 48b916e182
commit cb0d7e2902
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 248 additions and 147 deletions

View file

@ -22,18 +22,32 @@ use Stancl\Tenancy\Enums\RouteMode;
trait DealsWithRouteContexts trait DealsWithRouteContexts
{ {
/** /**
* Get route's middleware context (tenant, central or universal). * Get the middleware context of a route (tenant, central, or universal).
* The context is determined by the route's middleware.
* *
* If the route has the 'universal' middleware, the context is universal, * If the route has the 'universal' middleware, the context is universal,
* and the route is accessible from both contexts. * and the route is accessible from both contexts.
*
* The universal flag has the highest priority. * The universal flag has the highest priority.
* *
* If the route has the 'central' middleware, the context is central. * If you want a universal route to be accessible from the tenant context,
* If the route has the 'tenant' middleware, or any tenancy identification middleware, the context is tenant. * you still have to provide an identification middleware either using
* route-level middleware or in the global middleware stack.
* *
* If the route doesn't have any of the mentioned middleware, * If the 'tenant' group has identification middleware, you can use it in
* combination with the 'universal' flag, the route will still be universal.
*
* If the route has the 'tenant' middleware, or any tenancy identification
* middleware, the context is tenant (assuming the route doesn't also have
* the 'universal' flag).
*
* If the route has the 'central' middleware, the context is central.
*
* If the route doesn't have any of the mentioned flags/middleware,
* the context is determined by the `tenancy.default_route_mode` config. * the context is determined by the `tenancy.default_route_mode` config.
*
* If the default route mode is tenant, all unflagged routes will be tenant by default,
* but they will still have to have an identification midddleware (route-level
* or global) to be accessible. Same applies for universal default route mode.
*/ */
public static function getRouteMode(Route $route): RouteMode public static function getRouteMode(Route $route): RouteMode
{ {

View file

@ -93,8 +93,14 @@ test('central helper can be used in tenant requests', function (bool $enabled, b
} }
} }
})->with([ })->with([
['enabled' => false, 'shouldThrow' => true], [
['enabled' => true, 'shouldThrow' => false], false, // Disabled
true // Should throw
],
[
true, // Enabled
false // Should not throw
],
]); ]);
test('tenant run helper can be used on central requests', function (bool $enabled, bool $shouldThrow) { test('tenant run helper can be used on central requests', function (bool $enabled, bool $shouldThrow) {
@ -140,6 +146,12 @@ test('tenant run helper can be used on central requests', function (bool $enable
} }
} }
})->with([ })->with([
['enabled' => false, 'shouldThrow' => true], [
['enabled' => true, 'shouldThrow' => false], false, // Disabled
true // Should throw
],
[
true, // Enabled
false // Should not throw
],
]); ]);

View file

@ -331,19 +331,19 @@ class CustomInitializeTenancyByPath extends InitializeTenancyByPath
dataset('path identification types', [ dataset('path identification types', [
'kernel identification' => [ 'kernel identification' => [
'route_middleware' => ['universal'], ['universal'], // Route middleware
'global_middleware' => [InitializeTenancyByPath::class], [InitializeTenancyByPath::class], // Global Global middleware
], ],
'route-level identification' => [ 'route-level identification' => [
'route_middleware' => ['universal', InitializeTenancyByPath::class], ['universal', InitializeTenancyByPath::class], // Route middleware
'global_middleware' => [], [], // Global middleware
], ],
'kernel identification + defaulting to universal routes' => [ 'kernel identification + defaulting to universal routes' => [
'route_middleware' => [], [], // Route middleware
'global_middleware' => ['universal', InitializeTenancyByPath::class], ['universal', InitializeTenancyByPath::class], // Global middleware
], ],
'route-level identification + defaulting to universal routes' => [ 'route-level identification + defaulting to universal routes' => [
'route_middleware' => [InitializeTenancyByPath::class], [InitializeTenancyByPath::class], // Route middleware
'global_middleware' => ['universal'], ['universal'], // Global middleware
], ],
]); ]);

View file

@ -15,6 +15,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain; use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain; use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Exceptions\NotASubdomainException;
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData; use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\Models\Post; use Stancl\Tenancy\Tests\Etc\EarlyIdentification\Models\Post;
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains; use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
@ -22,6 +23,7 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
use Stancl\Tenancy\Middleware\InitializeTenancyByOriginHeader; use Stancl\Tenancy\Middleware\InitializeTenancyByOriginHeader;
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithMiddleware; use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithMiddleware;
use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithRouteMiddleware; use Stancl\Tenancy\Tests\Etc\EarlyIdentification\ControllerWithRouteMiddleware;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
beforeEach(function () { beforeEach(function () {
config()->set([ config()->set([
@ -35,6 +37,30 @@ beforeEach(function () {
}); });
}); });
dataset('identification_middleware', [
InitializeTenancyByDomain::class,
InitializeTenancyBySubdomain::class,
InitializeTenancyByDomainOrSubdomain::class,
InitializeTenancyByPath::class,
InitializeTenancyByRequestData::class,
]);
dataset('domain_identification_middleware', [
InitializeTenancyByDomain::class,
InitializeTenancyBySubdomain::class,
InitializeTenancyByDomainOrSubdomain::class,
]);
dataset('default_route_modes', [
RouteMode::TENANT,
RouteMode::CENTRAL,
]);
dataset('global_and_route_level_identification', [
false, // Route-level identification
true, // Global identification
]);
test('early identification works with path identification', function (bool $useKernelIdentification, RouteMode $defaultRouteMode) { test('early identification works with path identification', function (bool $useKernelIdentification, RouteMode $defaultRouteMode) {
$identificationMiddleware = InitializeTenancyByPath::class; $identificationMiddleware = InitializeTenancyByPath::class;
@ -117,14 +143,8 @@ test('early identification works with path identification', function (bool $useK
->assertOk() ->assertOk()
->assertContent($tenantPost->title . '-' . $tenantComment->comment); ->assertContent($tenantPost->title . '-' . $tenantComment->comment);
assertTenancyInitializedInEarlyIdentificationRequest(); assertTenancyInitializedInEarlyIdentificationRequest();
})->with([ })->with('global_and_route_level_identification')
'route-level identification' => false, ->with('default_route_modes');
'kernel identification' => true,
// Creates a matrix (multiple with())
])->with([
'default to tenant routes' => RouteMode::TENANT,
'default to central routes' => RouteMode::CENTRAL,
]);
test('early identification works with request data identification', function (string $type, bool $useKernelIdentification, RouteMode $defaultRouteMode) { test('early identification works with request data identification', function (string $type, bool $useKernelIdentification, RouteMode $defaultRouteMode) {
$identificationMiddleware = InitializeTenancyByRequestData::class; $identificationMiddleware = InitializeTenancyByRequestData::class;
@ -168,14 +188,8 @@ test('early identification works with request data identification', function (st
'using request header parameter' => 'header', 'using request header parameter' => 'header',
'using request query parameter' => 'queryParameter', 'using request query parameter' => 'queryParameter',
'using request cookie parameter' => 'cookie', 'using request cookie parameter' => 'cookie',
// Creates a matrix (multiple with()) ])->with('global_and_route_level_identification')->with('default_route_modes');
])->with([
'route-level identification' => false,
'kernel identification' => true,
])->with([
'default to tenant routes' => RouteMode::TENANT,
'default to central routes' => RouteMode::CENTRAL,
]);
test('early identification works with origin identification', function (bool $useKernelIdentification, RouteMode $defaultRouteMode) { test('early identification works with origin identification', function (bool $useKernelIdentification, RouteMode $defaultRouteMode) {
$identificationMiddleware = InitializeTenancyByOriginHeader::class; $identificationMiddleware = InitializeTenancyByOriginHeader::class;
@ -209,85 +223,111 @@ test('early identification works with origin identification', function (bool $us
$response = pest()->post('/tenant-route', headers: ['Origin' => 'foo.localhost']); $response = pest()->post('/tenant-route', headers: ['Origin' => 'foo.localhost']);
$response->assertOk()->assertSee('token:' . $tenantKey); $response->assertOk()->assertSee('token:' . $tenantKey);
})->with([ })->with('global_and_route_level_identification')->with('default_route_modes');
'route-level identification' => false,
'kernel identification' => true,
])->with([
'default to tenant routes' => RouteMode::TENANT,
'default to central routes' => RouteMode::CENTRAL,
]);
test('early identification works with domain identification', function (string $middleware, string $domain, bool $useKernelIdentification, RouteMode $defaultRouteMode) {
config(['tenancy.default_route_mode' => $defaultRouteMode]);
if ($useKernelIdentification) {
$controller = ControllerWithMiddleware::class;
app(Kernel::class)->pushMiddleware($middleware);
app(Kernel::class)->pushMiddleware(PreventAccessFromUnwantedDomains::class);
} else {
$controller = ControllerWithRouteMiddleware::class;
RouteFacade::middlewareGroup('tenant', [$middleware, PreventAccessFromUnwantedDomains::class]);
}
// Tenant route
$tenantRoute = RouteFacade::get('/tenant-route', [$controller, 'index']);
// Central route
$centralRoute = RouteFacade::get('/central-route', function () {
return 'central route';
});
$defaultToTenantRoutes = $defaultRouteMode === RouteMode::TENANT;
// Test defaulting to route mode (central/tenant context)
if ($useKernelIdentification) {
$routeThatShouldReceiveMiddleware = $defaultToTenantRoutes ? $centralRoute : $tenantRoute;
$routeThatShouldReceiveMiddleware->middleware($defaultToTenantRoutes ? 'central' : 'tenant');
} elseif (! $defaultToTenantRoutes) {
$tenantRoute->middleware('tenant');
} else {
// Route-level identification + defaulting to tenant routes
// We still have to apply the tenant middleware to the routes, so they aren't really tenant by default
$tenantRoute->middleware([$middleware, PreventAccessFromUnwantedDomains::class]);
}
test('early identification works with domain identification', function (string $middleware, bool $useKernelIdentification) {
$tenant = Tenant::create(); $tenant = Tenant::create();
$tenant->domains()->create([ // Create domain and a subdomain for the tenant
'domain' => $domain, $tenant->createDomain('foo.test');
]); $tenant->createDomain('foo');
if ($domain === 'foo') { if ($useKernelIdentification) {
$domain = 'foo.localhost'; app(Kernel::class)->pushMiddleware($middleware);
app(Kernel::class)->pushMiddleware(PreventAccessFromUnwantedDomains::class);
RouteFacade::get('/tenant-route', [ControllerWithMiddleware::class, 'index'])->middleware('tenant');
} else {
RouteFacade::get('/tenant-route', [ControllerWithRouteMiddleware::class, 'index'])->middleware([$middleware, PreventAccessFromUnwantedDomains::class]);
} }
pest()->get('http://localhost/central-route')->assertOk()->assertContent('central route'); // Central route is accessible $domainUrl = 'http://foo.test/tenant-route';
$subdomainUrl = str(config('app.url'))->replaceFirst('://', "://foo.")->toString() . '/tenant-route';
$response = pest()->get("http://{$domain}/tenant-route"); $tenantUrls = Arr::wrap(match ($middleware) {
InitializeTenancyByDomain::class => $domainUrl,
InitializeTenancyBySubdomain::class => $subdomainUrl,
InitializeTenancyByDomainOrSubdomain::class => [$domainUrl, $subdomainUrl], // Domain or subdomain -- try visiting both
});
foreach ($tenantUrls as $url) {
$response = pest()->get($url);
if ($defaultToTenantRoutes === $useKernelIdentification || $useKernelIdentification) {
$response->assertOk(); $response->assertOk();
assertTenancyInitializedInEarlyIdentificationRequest(); assertTenancyInitializedInEarlyIdentificationRequest();
} elseif (! $defaultToTenantRoutes) {
$response->assertNotFound();
assertTenancyInitializedInEarlyIdentificationRequest(false);
}
// Expect tenancy is initialized (or not) for the right tenant at the tenant route // Expect tenancy is initialized (or not) for the right tenant at the tenant route
expect($response->getContent())->toBe('token:' . tenant()->getTenantKey()); expect($response->getContent())->toBe('token:' . tenant()->getTenantKey());
})->with([ }
'domain identification' => ['middleware' => InitializeTenancyByDomain::class, 'domain' => 'foo.test'], })->with('domain_identification_middleware')
'subdomain identification' => ['middleware' => InitializeTenancyBySubdomain::class, 'domain' => 'foo'], ->with('global_and_route_level_identification');
'domainOrSubdomain identification using domain' => ['middleware' => InitializeTenancyByDomainOrSubdomain::class, 'domain' => 'foo.test'],
'domainOrSubdomain identification using subdomain' => ['middleware' => InitializeTenancyByDomainOrSubdomain::class, 'domain' => 'foo'], test('using different default route modes works with global domain identification', function(string $middleware, RouteMode $defaultRouteMode) {
// Creates a matrix (multiple with()) config(['tenancy.default_route_mode' => $defaultRouteMode]);
])->with([
'route-level identification' => false, $tenant = Tenant::create();
'kernel identification' => true,
])->with([ // Create domain and a subdomain for the tenant
'default to tenant routes' => RouteMode::TENANT, $tenant->createDomain('foo.test');
'default to central routes' => RouteMode::CENTRAL, $tenant->createDomain('foo');
]);
// Create central and tenant routes, without any identification middleware or tags
$centralRoute = RouteFacade::get('/central-route', fn () => 'central route');
RouteFacade::get('/tenant-route', [ControllerWithMiddleware::class, 'index']);
// Add the domain identification middleware to the kernel MW
app(Kernel::class)->pushMiddleware($middleware);
app(Kernel::class)->pushMiddleware(PreventAccessFromUnwantedDomains::class);
$domainUrl = 'http://foo.test/tenant-route';
$subdomainUrl = str(config('app.url'))->replaceFirst('://', "://foo.")->toString() . '/tenant-route';
$tenantUrls = Arr::wrap(match ($middleware) {
InitializeTenancyByDomain::class => $domainUrl,
InitializeTenancyBySubdomain::class => $subdomainUrl,
InitializeTenancyByDomainOrSubdomain::class => [$domainUrl, $subdomainUrl], // Domain or subdomain -- try visiting both
});
if ($defaultRouteMode === RouteMode::TENANT) {
// When defaulting to tenant routes and using kernel identification,
// the central route should not be accessible if not flagged as central.
// Since central-route is considered tenant by default, and there's tenant ID MW,
// expect that an exception specific to that ID MW to be thrown when trying to access the route.
$exception = match ($middleware) {
InitializeTenancyByDomain::class => TenantCouldNotBeIdentifiedOnDomainException::class,
InitializeTenancyBySubdomain::class => NotASubdomainException::class,
InitializeTenancyByDomainOrSubdomain::class => NotASubdomainException::class,
};
expect(fn () => $this->withoutExceptionHandling()->get('http://localhost/central-route'))->toThrow($exception);
// Flagging the central route as central should make it accessible,
// even if the default route mode is tenant
$centralRoute = $centralRoute->middleware('central');
pest()->get('http://localhost/central-route')->assertOk()->assertSee('central route');
}
foreach ($tenantUrls as $url) {
$response = pest()->get($url);
// If the default route mode is tenant, only the tenant route should be accessible
// and tenancy should be initialized using early identification for the correct tenant
if ($defaultRouteMode === RouteMode::TENANT) {
$response->assertOk();
assertTenancyInitializedInEarlyIdentificationRequest();
// Expect tenancy is initialized for the right tenant at the tenant route
expect($response->getContent())->toBe('token:' . tenant()->getTenantKey());
} else {
$response->assertNotFound();
}
}
})->with('domain_identification_middleware')
->with('default_route_modes');
test('the tenant parameter is only removed from tenant routes when using path identification', function (bool $kernelIdentification, bool $pathIdentification) { test('the tenant parameter is only removed from tenant routes when using path identification', function (bool $kernelIdentification, bool $pathIdentification) {
if ($kernelIdentification) { if ($kernelIdentification) {
@ -373,62 +413,97 @@ test('the tenant parameter is only removed from tenant routes when using path id
} }
} }
})->with([ })->with([
'kernel path identification' => ['kernelIdentification' => true, 'pathIdentification' => true], 'kernel path identification' => [
'route-level path identification' => ['kernelIdentification' => false, 'pathIdentification' => true], true, // Kernel identification
'kernel domain identification' => ['kernelIdentification' => true, 'pathIdentification' => false], true // Path identification
'route-level domain identification' => ['kernelIdentification' => false, 'pathIdentification' => false], ],
'route-level path identification' => [
false, // Kernel identification
true // Path identification
],
'kernel domain identification' => [
true,
false // Path identification
],
'route-level domain identification' => [
false, // Kernel identification
false // Path identification
],
]); ]);
test('route level identification is prioritized over kernel identification', function ( test('route level domain identification is prioritized over kernel identification', function (
string|array $kernelIdentificationMiddleware, string $kernelIdentificationMiddleware,
string|array $routeIdentificationMiddleware, string $routeIdentificationMiddleware,
string $routeUri,
string $domainToVisit,
string|null $domain = null,
RouteMode $defaultRouteMode, RouteMode $defaultRouteMode,
) { ) {
$tenant = Tenant::create(); $tenant = Tenant::create();
$domainToVisit = str_replace('{tenantKey}', $tenant->getTenantKey(), $domainToVisit);
config(['tenancy.default_route_mode' => $defaultRouteMode]); config(['tenancy.default_route_mode' => $defaultRouteMode]);
if ($domain) { // Subdomain
$tenant->domains()->create(['domain' => str_replace('{tenantKey}', $tenant->getTenantKey(), $domain)]); $tenant->createDomain($subdomain = $tenant->getTenantKey());
$tenant->createDomain($domain = $subdomain . '.test');
app(Kernel::class)->pushMiddleware(PreventAccessFromUnwantedDomains::class)->pushMiddleware($kernelIdentificationMiddleware);
// We're testing *non-early* route-level identification so that we can assert that early kernel identification got skipped
// Also, ignore the defaulting when the identification MW is applied directly on the route
// Because the route is automatically considered tenant if it has identification middleware (unless it also has the 'universal' middleware)
RouteFacade::get('tenant-route', [ControllerWithMiddleware::class, 'index'])
->middleware([PreventAccessFromUnwantedDomains::class, $routeIdentificationMiddleware]);
$domainIdUrl = "http://{$domain}/tenant-route";
$subdomainIdUrl = str(config('app.url'))->replaceFirst('://', "://{$subdomain}.")->append("/tenant-route")->toString();
$urlsToVisit = Arr::wrap(match ($routeIdentificationMiddleware) {
InitializeTenancyByDomain::class => $domainIdUrl,
InitializeTenancyBySubdomain::class => $subdomainIdUrl,
InitializeTenancyByDomainOrSubdomain::class => [$domainIdUrl, $subdomainIdUrl], // Domain or subdomain -- try visiting both
});
foreach ($urlsToVisit as $url) {
pest()->get($url)->assertOk();
// Kernel (early) identification skipped
expect(app()->make('controllerRunsInTenantContext'))->toBeFalse();
}
})->with('identification_middleware')
->with('domain_identification_middleware')
->with('default_route_modes');
test('route level path and request data identification is prioritized over kernel identification', function (
string $kernelIdentificationMiddleware,
string $routeIdentificationMiddleware,
RouteMode $defaultRouteMode,
) {
$tenant = Tenant::create();
config(['tenancy.default_route_mode' => $defaultRouteMode]);
if (in_array($kernelIdentificationMiddleware, config('tenancy.identification.domain_identification_middleware'))) {
// If a domain identification middleware is used, the prevent access MW is used too
app(Kernel::class)->pushMiddleware(PreventAccessFromUnwantedDomains::class);
} }
foreach (Arr::wrap($kernelIdentificationMiddleware) as $identificationMiddleware) { app(Kernel::class)->pushMiddleware($kernelIdentificationMiddleware);
app(Kernel::class)->pushMiddleware($identificationMiddleware);
}
// We're testing *non-early* route-level identification so that we can assert that early kernel identification got skipped // We're testing *non-early* route-level identification so that we can assert that early kernel identification got skipped
// Also, ignore the defaulting when the identification MW is applied directly on the route // Also, ignore the defaulting when the identification MW is applied directly on the route
// The route is automatically considered tenant if it has identification middleware (unless it also has the 'universal' middleware) // The route is automatically considered tenant if it has identification middleware (unless it also has the 'universal' middleware)
RouteFacade::get($routeUri, [ControllerWithMiddleware::class, 'index'])->middleware($routeIdentificationMiddleware); $route = RouteFacade::get('/tenant-route', [ControllerWithMiddleware::class, 'index'])->middleware($routeIdentificationMiddleware)->name('tenant-route');
pest()->get($domainToVisit)->assertOk(); if ($routeIdentificationMiddleware === InitializeTenancyByPath::class) {
$route = $route->prefix('{tenant}');
}
pest()->get(route('tenant-route', ['tenant' => $tenant->getTenantKey()]))->assertOk();
// Kernel (early) identification skipped // Kernel (early) identification skipped
expect(app()->make('controllerRunsInTenantContext'))->toBeFalse(); expect(app()->make('controllerRunsInTenantContext'))->toBeFalse();
})->with([ })->with('identification_middleware')
'kernel request data identification mw' => ['kernelMiddleware' => InitializeTenancyByRequestData::class],
'kernel path identification mw' => ['kernelMiddleware' => InitializeTenancyByPath::class],
'kernel domain identification mw' => ['kernelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class]],
'kernel subdomain identification mw' => ['kernelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class]],
'kernel domainOrSubdomain identification mw using domain' => ['kernelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomainOrSubdomain::class]],
'kernel domainOrSubdomain identification mw using subdomain' => ['kernelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomainOrSubdomain::class]],
// Creates a matrix (multiple with())
])->with([
'route level request data identification mw' => ['routeLevelMiddleware' => InitializeTenancyByRequestData::class, 'routeUri' => '/tenant-route', 'domainToVisit' => 'http://localhost/tenant-route?tenant={tenantKey}', 'domain' => null],
'route level path identification mw' => ['routeLevelMiddleware' => InitializeTenancyByPath::class, 'routeUri' => '/{tenant}/tenant-route', 'domainToVisit' => 'http://localhost/{tenantKey}/tenant-route', 'domain' => null],
'route level domain identification mw' => ['routeLevelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class], 'routeUri' => '/tenant-route', 'domainToVisit' => 'http://{tenantKey}.test/tenant-route', 'domain' => '{tenantKey}.test'],
'route level subdomain identification mw' => ['routeLevelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], 'routeUri' => '/tenant-route', 'domainToVisit' => 'http://{tenantKey}.localhost/tenant-route', 'domain' => '{tenantKey}'],
'route level domainOrSubdomain identification mw using domain' => ['routeLevelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomainOrSubdomain::class], 'routeUri' => '/tenant-route', 'domainToVisit' => 'http://{tenantKey}.test/tenant-route', 'domain' => '{tenantKey}.test'],
'route level domainOrSubdomain identification mw using subdomain' => ['routeLevelMiddleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomainOrSubdomain::class], 'routeUri' => '/tenant-route', 'domainToVisit' => 'http://{tenantKey}.localhost/tenant-route', 'domain' => '{tenantKey}'],
])
->with([ ->with([
'default to tenant routes' => RouteMode::TENANT, 'route level request data identification' => InitializeTenancyByRequestData::class,
'default to central routes' => RouteMode::CENTRAL, 'route level path identification' => InitializeTenancyByPath::class,
]); ])->with('default_route_modes');
function assertTenancyInitializedInEarlyIdentificationRequest(bool $expect = true): void function assertTenancyInitializedInEarlyIdentificationRequest(bool $expect = true): void
{ {

View file

@ -180,8 +180,8 @@ test('kernel PreventAccessFromUnwantedDomains does not get skipped when route le
]); ]);
test('placement of domain identification and access prevention middleware can get mixed', function ( test('placement of domain identification and access prevention middleware can get mixed', function (
array $globalMiddleware,
array $routeMiddleware, array $routeMiddleware,
array $globalMiddleware,
array $centralRouteMiddleware array $centralRouteMiddleware
) { ) {
config([ config([
@ -214,16 +214,16 @@ test('placement of domain identification and access prevention middleware can ge
expect(tenancy()->initialized)->toBeFalse(); expect(tenancy()->initialized)->toBeFalse();
})->with([ })->with([
'route-level identification, kernel access prevention' => [ 'route-level identification, kernel access prevention' => [
'global_middleware' => [PreventAccessFromUnwantedDomains::class], [InitializeTenancyBySubdomain::class], // Route middleware
'route_middleware' => [InitializeTenancyBySubdomain::class], [PreventAccessFromUnwantedDomains::class], // Global middleware
], ],
'kernel identification, kernel access prevention' => [ 'kernel identification, kernel access prevention' => [
'global_middleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], [], // Route middleware
'route_middleware' => [], [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], // Global middleware
], ],
'route-level identification, route-level access prevention' => [ 'route-level identification, route-level access prevention' => [
'global_middleware' => [], [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], // Route middleware
'route_middleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], [], // Global middleware
], ],
// Creates a matrix (multiple with()) // Creates a matrix (multiple with())
])->with([ ])->with([

View file

@ -123,11 +123,11 @@ test('single domain tenant can be identified by domain or subdomain', function (
expect(tenant('id'))->toBe($tenant->id); expect(tenant('id'))->toBe($tenant->id);
})->with([ })->with([
[ [
'domain' => 'acme.localhost', 'acme.localhost', // Domain
'identification middleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class], [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class], // Identification middleware
], ],
[ [
'subdomain' => 'acme', 'acme', // Subdomain
'identification middleware' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class], // Identification middleware
], ],
]); ]);

View file

@ -327,20 +327,20 @@ foreach ([
] as $datasetName => $middleware) { ] as $datasetName => $middleware) {
dataset($datasetName, [ dataset($datasetName, [
'kernel identification' => [ 'kernel identification' => [
'route_middleware' => ['universal'], ['universal'], // Route middleware
'global_middleware' => $middleware, $middleware, // Global middleware
], ],
'route-level identification' => [ 'route-level identification' => [
'route_middleware' => ['universal', ...$middleware], ['universal', ...$middleware], // Route middleware
'global_middleware' => [], [], // Global middleware
], ],
'kernel identification + defaulting to universal routes' => [ 'kernel identification + defaulting to universal routes' => [
'route_middleware' => [], [], // Route middleware
'global_middleware' => ['universal', ...$middleware], ['universal', ...$middleware], // Global middleware
], ],
'route-level identification + defaulting to universal routes' => [ 'route-level identification + defaulting to universal routes' => [
'route_middleware' => $middleware, $middleware, // Route middleware
'global_middleware' => ['universal'], ['universal'], // Global middleware
], ],
]); ]);
} }