mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 08:24:04 +00:00
[4.x] Migrate tests to Pest (#884)
* Add Pest dependencies * Add base Pest file * Convert test cases * Remove non-compound imports * Adopt expectation API * Optimize uses * Shift cleanup * phpunit -> pest * Fix tests in PR #884 PHPUnit to Pest Converter (#885) * fixed tests, remove method duplications, restore necessary inner classes * Update CommandsTest.php * temporary checks run on `shift-64622` on branch. * fixed `TestSeeder` class not resolved * fixed messed up names * removed `uses` from individual files and add it in `Pest` * extract tests to helpers * use pest dataset * Update AutomaticModeTest.php * newline * todo convention * resolve reviews * added `// todo@tests` * remove shift branch from CI workflow Co-authored-by: Samuel Štancl <samuel@archte.ch> * check if I have write permission * Convert newly added tests to Pest Co-authored-by: Shift <shift@laravelshift.com> Co-authored-by: Abrar Ahmad <abrar.dev99@gmail.com>
This commit is contained in:
parent
69de181b7d
commit
b47c5549ef
32 changed files with 3010 additions and 3478 deletions
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Stancl\Tenancy\Tests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\CarbonInterval;
|
||||
use Closure;
|
||||
use Illuminate\Auth\SessionGuard;
|
||||
use Illuminate\Foundation\Auth\User as Authenticable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
|
@ -26,249 +22,234 @@ use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
|||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
use Illuminate\Foundation\Auth\User as Authenticable;
|
||||
|
||||
class TenantUserImpersonationTest extends TestCase
|
||||
beforeEach(function () {
|
||||
$this->artisan('migrate', [
|
||||
'--path' => __DIR__ . '/../assets/impersonation-migrations',
|
||||
'--realpath' => true,
|
||||
])->assertExitCode(0);
|
||||
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.features' => [
|
||||
UserImpersonation::class,
|
||||
],
|
||||
]);
|
||||
|
||||
Event::listen(
|
||||
TenantCreated::class,
|
||||
JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener()
|
||||
);
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
|
||||
config(['auth.providers.users.model' => ImpersonationUser::class]);
|
||||
});
|
||||
|
||||
test('tenant user can be impersonated on a tenant domain', function () {
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group(getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertRedirect('http://foo.localhost/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
$this->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertRedirect('http://foo.localhost/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
});
|
||||
|
||||
test('tenant user can be impersonated on a tenant path', function () {
|
||||
makeLoginRoute();
|
||||
|
||||
Route::middleware(InitializeTenancyByPath::class)->prefix('/{tenant}')->group(getRoutes(false));
|
||||
|
||||
$tenant = Tenant::create([
|
||||
'id' => 'acme',
|
||||
'tenancy_db_name' => 'db' . Str::random(16),
|
||||
]);
|
||||
migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('/acme/dashboard')
|
||||
->assertRedirect('/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/acme/dashboard');
|
||||
$this->get('/acme/impersonate/' . $token->token)
|
||||
->assertRedirect('/acme/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('/acme/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
});
|
||||
|
||||
test('tokens have a limited ttl', function () {
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group(getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
$token->update([
|
||||
'created_at' => Carbon::now()->subtract(CarbonInterval::make('100s')),
|
||||
]);
|
||||
|
||||
$this->followingRedirects()
|
||||
->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertStatus(403);
|
||||
});
|
||||
|
||||
test('tokens are deleted after use', function () {
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group(getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
|
||||
$this->assertNotNull(ImpersonationToken::find($token->token));
|
||||
|
||||
$this->followingRedirects()
|
||||
->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
|
||||
expect(ImpersonationToken::find($token->token))->toBeNull();
|
||||
});
|
||||
|
||||
test('impersonation works with multiple models and guards', function () {
|
||||
config([
|
||||
'auth.guards.another' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'another_users',
|
||||
],
|
||||
'auth.providers.another_users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => AnotherImpersonationUser::class,
|
||||
],
|
||||
]);
|
||||
|
||||
Auth::extend('another', function ($app, $name, array $config) {
|
||||
return new SessionGuard($name, Auth::createUserProvider($config['provider']), session());
|
||||
});
|
||||
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group(getRoutes(true, 'another'));
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return AnotherImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertRedirect('http://foo.localhost/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard', 'another');
|
||||
$this->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertRedirect('http://foo.localhost/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
|
||||
Tenant::first()->run(function () {
|
||||
expect(auth()->guard('another')->user()->name)->toBe('Joe');
|
||||
expect(auth()->guard('web')->user())->toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
function migrateTenants()
|
||||
{
|
||||
protected function migrateTenants()
|
||||
{
|
||||
$this->artisan('tenants:migrate')->assertExitCode(0);
|
||||
}
|
||||
test()->artisan('tenants:migrate')->assertExitCode(0);
|
||||
}
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
function makeLoginRoute()
|
||||
{
|
||||
Route::get('/login', function () {
|
||||
return 'Please log in';
|
||||
})->name('login');
|
||||
}
|
||||
|
||||
$this->artisan('migrate', [
|
||||
'--path' => __DIR__ . '/../assets/impersonation-migrations',
|
||||
'--realpath' => true,
|
||||
])->assertExitCode(0);
|
||||
function getRoutes($loginRoute = true, $authGuard = 'web'): Closure
|
||||
{
|
||||
return function () use ($loginRoute, $authGuard) {
|
||||
if ($loginRoute) {
|
||||
test()->makeLoginRoute();
|
||||
}
|
||||
|
||||
config([
|
||||
'tenancy.bootstrappers' => [
|
||||
DatabaseTenancyBootstrapper::class,
|
||||
],
|
||||
'tenancy.features' => [
|
||||
UserImpersonation::class,
|
||||
],
|
||||
]);
|
||||
Route::get('/dashboard', function () use ($authGuard) {
|
||||
return 'You are logged in as ' . auth()->guard($authGuard)->user()->name;
|
||||
})->middleware('auth:' . $authGuard);
|
||||
|
||||
Event::listen(
|
||||
TenantCreated::class,
|
||||
JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
||||
return $event->tenant;
|
||||
})->toListener()
|
||||
);
|
||||
|
||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||
|
||||
config(['auth.providers.users.model' => ImpersonationUser::class]);
|
||||
}
|
||||
|
||||
public function makeLoginRoute()
|
||||
{
|
||||
Route::get('/login', function () {
|
||||
return 'Please log in';
|
||||
})->name('login');
|
||||
}
|
||||
|
||||
public function getRoutes($loginRoute = true, $authGuard = 'web'): Closure
|
||||
{
|
||||
return function () use ($loginRoute, $authGuard) {
|
||||
if ($loginRoute) {
|
||||
$this->makeLoginRoute();
|
||||
}
|
||||
|
||||
Route::get('/dashboard', function () use ($authGuard) {
|
||||
return 'You are logged in as ' . auth()->guard($authGuard)->user()->name;
|
||||
})->middleware('auth:' . $authGuard);
|
||||
|
||||
Route::get('/impersonate/{token}', function ($token) {
|
||||
return UserImpersonation::makeResponse($token);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function tenant_user_can_be_impersonated_on_a_tenant_domain()
|
||||
{
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group($this->getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
$this->migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
Route::get('/impersonate/{token}', function ($token) {
|
||||
return UserImpersonation::makeResponse($token);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertRedirect('http://foo.localhost/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
$this->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertRedirect('http://foo.localhost/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function tenant_user_can_be_impersonated_on_a_tenant_path()
|
||||
{
|
||||
$this->makeLoginRoute();
|
||||
|
||||
Route::middleware(InitializeTenancyByPath::class)->prefix('/{tenant}')->group($this->getRoutes(false));
|
||||
|
||||
$tenant = Tenant::create([
|
||||
'id' => 'acme',
|
||||
'tenancy_db_name' => 'db' . Str::random(16),
|
||||
]);
|
||||
$this->migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('/acme/dashboard')
|
||||
->assertRedirect('/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/acme/dashboard');
|
||||
$this->get('/acme/impersonate/' . $token->token)
|
||||
->assertRedirect('/acme/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('/acme/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function tokens_have_a_limited_ttl()
|
||||
{
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group($this->getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
$this->migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
$token->update([
|
||||
'created_at' => Carbon::now()->subtract(CarbonInterval::make('100s')),
|
||||
]);
|
||||
|
||||
$this->followingRedirects()
|
||||
->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertStatus(403);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function tokens_are_deleted_after_use()
|
||||
{
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group($this->getRoutes());
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
$this->migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return ImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard');
|
||||
|
||||
$this->assertNotNull(ImpersonationToken::find($token->token));
|
||||
|
||||
$this->followingRedirects()
|
||||
->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
|
||||
$this->assertNull(ImpersonationToken::find($token->token));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function impersonation_works_with_multiple_models_and_guards()
|
||||
{
|
||||
config([
|
||||
'auth.guards.another' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'another_users',
|
||||
],
|
||||
'auth.providers.another_users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => AnotherImpersonationUser::class,
|
||||
],
|
||||
]);
|
||||
|
||||
Auth::extend('another', function ($app, $name, array $config) {
|
||||
return new SessionGuard($name, Auth::createUserProvider($config['provider']), session());
|
||||
});
|
||||
|
||||
Route::middleware(InitializeTenancyByDomain::class)->group($this->getRoutes(true, 'another'));
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenant->domains()->create([
|
||||
'domain' => 'foo.localhost',
|
||||
]);
|
||||
$this->migrateTenants();
|
||||
$user = $tenant->run(function () {
|
||||
return AnotherImpersonationUser::create([
|
||||
'name' => 'Joe',
|
||||
'email' => 'joe@local',
|
||||
'password' => bcrypt('secret'),
|
||||
]);
|
||||
});
|
||||
|
||||
// We try to visit the dashboard directly, before impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertRedirect('http://foo.localhost/login');
|
||||
|
||||
// We impersonate the user
|
||||
$token = tenancy()->impersonate($tenant, $user->id, '/dashboard', 'another');
|
||||
$this->get('http://foo.localhost/impersonate/' . $token->token)
|
||||
->assertRedirect('http://foo.localhost/dashboard');
|
||||
|
||||
// Now we try to visit the dashboard directly, after impersonating the user.
|
||||
$this->get('http://foo.localhost/dashboard')
|
||||
->assertSuccessful()
|
||||
->assertSee('You are logged in as Joe');
|
||||
|
||||
Tenant::first()->run(function () {
|
||||
$this->assertSame('Joe', auth()->guard('another')->user()->name);
|
||||
$this->assertSame(null, auth()->guard('web')->user());
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class ImpersonationUser extends Authenticable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue