1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 23:44:03 +00:00

Merge branch 'master' into 515-complete

This commit is contained in:
Samuel Štancl 2022-10-25 12:54:21 +02:00
commit b3902bcf29
34 changed files with 453 additions and 144 deletions

View file

@ -2,24 +2,28 @@
declare(strict_types=1);
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Tests\Etc\User;
use Stancl\JobPipeline\JobPipeline;
use Stancl\Tenancy\Tests\Etc\Tenant;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;
use Stancl\JobPipeline\JobPipeline;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Jobs\DeleteDomains;
use Illuminate\Support\Facades\Artisan;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Jobs\DeleteDatabase;
use Illuminate\Database\DatabaseManager;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Events\TenantDeleted;
use Stancl\Tenancy\Tests\Etc\TestSeeder;
use Stancl\Tenancy\Events\DeletingTenant;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Tests\Etc\ExampleSeeder;
use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Tests\Etc\TestSeeder;
use Stancl\Tenancy\Tests\Etc\User;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
beforeEach(function () {
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
@ -167,7 +171,9 @@ test('install command works', function () {
mkdir($dir, 0777, true);
}
pest()->artisan('tenancy:install');
pest()->artisan('tenancy:install')
->expectsConfirmation('Would you like to show your support by starring the project on GitHub?', 'no')
->assertExitCode(0);
expect(base_path('routes/tenant.php'))->toBeFile();
expect(base_path('config/tenancy.php'))->toBeFile();
expect(app_path('Providers/TenancyServiceProvider.php'))->toBeFile();
@ -210,7 +216,8 @@ test('run command with array of tenants works', function () {
test('link command works', function() {
$tenantId1 = Tenant::create()->getTenantKey();
$tenantId2 = Tenant::create()->getTenantKey();
pest()->artisan('tenants:link');
pest()->artisan('tenants:link')
->assertExitCode(0);
$this->assertDirectoryExists(storage_path("tenant-$tenantId1/app/public"));
$this->assertEquals(storage_path("tenant-$tenantId1/app/public/"), readlink(public_path("public-$tenantId1")));
@ -220,7 +227,7 @@ test('link command works', function() {
pest()->artisan('tenants:link', [
'--remove' => true,
]);
])->assertExitCode(0);
$this->assertDirectoryDoesNotExist(public_path("public-$tenantId1"));
$this->assertDirectoryDoesNotExist(public_path("public-$tenantId2"));
@ -252,8 +259,9 @@ test('run command works when sub command asks questions and accepts arguments',
pest()->artisan("tenants:run --tenants=$id 'user:addwithname Abrar' ")
->expectsQuestion('What is your email?', 'email@localhost')
->expectsOutput("Tenant: $id")
->expectsOutput("User created: Abrar(email@localhost)");
->expectsOutputToContain("Tenant: $id.")
->expectsOutput("User created: Abrar(email@localhost)")
->assertExitCode(0);
// Assert we are in central context
expect(tenancy()->initialized)->toBeFalse();
@ -267,6 +275,47 @@ test('run command works when sub command asks questions and accepts arguments',
expect($user->email)->toBe('email@localhost');
});
test('migrate fresh command only deletes tenant databases if drop_tenant_databases_on_migrate_fresh is true', function (bool $dropTenantDBsOnMigrateFresh) {
Event::listen(DeletingTenant::class,
JobPipeline::make([DeleteDomains::class])->send(function (DeletingTenant $event) {
return $event->tenant;
})->shouldBeQueued(false)->toListener()
);
Event::listen(
TenantDeleted::class,
JobPipeline::make([DeleteDatabase::class])->send(function (TenantDeleted $event) {
return $event->tenant;
})->shouldBeQueued(false)->toListener()
);
config(['tenancy.database.drop_tenant_databases_on_migrate_fresh' => $dropTenantDBsOnMigrateFresh]);
$shouldHaveDBAfterMigrateFresh = ! $dropTenantDBsOnMigrateFresh;
/** @var Tenant[] $tenants */
$tenants = [
Tenant::create(),
Tenant::create(),
Tenant::create(),
];
$tenantHasDatabase = fn (Tenant $tenant) => $tenant->database()->manager()->databaseExists($tenant->database()->getName());
foreach ($tenants as $tenant) {
expect($tenantHasDatabase($tenant))->toBeTrue();
}
pest()->artisan('migrate:fresh', [
'--force' => true,
'--path' => __DIR__ . '/../assets/migrations',
'--realpath' => true,
]);
foreach ($tenants as $tenant) {
expect($tenantHasDatabase($tenant))->toBe($shouldHaveDBAfterMigrateFresh);
}
})->with([true, false]);
// todo@tests
function runCommandWorks(): void
{

View file

@ -22,9 +22,7 @@ test('database can be created after tenant creation', function () {
})->toListener());
$tenant = Tenant::create();
$manager = app(MySQLDatabaseManager::class);
$manager->setConnection('mysql');
$manager = $tenant->database()->manager();
expect($manager->databaseExists($tenant->database()->getName()))->toBeTrue();
});

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Database\Concerns\MaintenanceMode;
use Illuminate\Support\Facades\Route;
use Stancl\Tenancy\Middleware\CheckTenantForMaintenanceMode;
@ -32,6 +33,20 @@ test('tenants can be in maintenance mode', function () {
pest()->get('http://acme.localhost/foo')->assertStatus(200);
});
test('maintenance mode events are fired', function () {
$tenant = MaintenanceTenant::create();
Event::fake();
$tenant->putDownForMaintenance();
Event::assertDispatched(\Stancl\Tenancy\Events\TenantMaintenanceModeEnabled::class);
$tenant->bringUpFromMaintenance();
Event::assertDispatched(\Stancl\Tenancy\Events\TenantMaintenanceModeDisabled::class);
});
test('tenants can be put into maintenance mode using artisan commands', function() {
Route::get('/foo', function () {
return 'bar';
@ -44,12 +59,18 @@ test('tenants can be put into maintenance mode using artisan commands', function
pest()->get('http://acme.localhost/foo')->assertStatus(200);
pest()->artisan('tenants:down')
->expectsOutputToContain('Tenants are now in maintenance mode.')
->assertExitCode(0);
Artisan::call('tenants:down');
tenancy()->end(); // End tenancy before making a request
pest()->get('http://acme.localhost/foo')->assertStatus(503);
Artisan::call('tenants:up');
pest()->artisan('tenants:up')
->expectsOutputToContain('Tenants are now out of maintenance mode.')
->assertExitCode(0);
tenancy()->end(); // End tenancy before making a request
pest()->get('http://acme.localhost/foo')->assertStatus(200);

View file

@ -20,19 +20,18 @@ beforeEach(function () {
afterEach(function () {
InitializeTenancyByRequestData::$header = 'X-Tenant';
InitializeTenancyByRequestData::$cookie = 'X-Tenant';
InitializeTenancyByRequestData::$queryParameter = 'tenant';
});
test('header identification works', function () {
InitializeTenancyByRequestData::$header = 'X-Tenant';
$tenant = Tenant::create();
$tenant2 = Tenant::create();
$this
->withoutExceptionHandling()
->get('test', [
'X-Tenant' => $tenant->id,
])
->withHeader('X-Tenant', $tenant->id)
->get('test')
->assertSee($tenant->id);
});
@ -40,10 +39,20 @@ test('query parameter identification works', function () {
InitializeTenancyByRequestData::$queryParameter = 'tenant';
$tenant = Tenant::create();
$tenant2 = Tenant::create();
$this
->withoutExceptionHandling()
->get('test?tenant=' . $tenant->id)
->assertSee($tenant->id);
});
test('cookie identification works', function () {
InitializeTenancyByRequestData::$cookie = 'X-Tenant';
$tenant = Tenant::create();
$this
->withoutExceptionHandling()
->withUnencryptedCookie('X-Tenant', $tenant->id)
->get('test',)
->assertSee($tenant->id);
});

View file

@ -9,6 +9,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Stancl\JobPipeline\JobPipeline;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Database\Contracts\StatefulTenantDatabaseManager;
use Stancl\Tenancy\Database\DatabaseManager;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
@ -37,7 +38,10 @@ test('databases can be created and deleted', function ($driver, $databaseManager
$name = 'db' . pest()->randomString();
$manager = app($databaseManager);
$manager->setConnection($driver);
if ($manager instanceof StatefulTenantDatabaseManager) {
$manager->setConnection($driver);
}
expect($manager->databaseExists($name))->toBeFalse();

View file

@ -18,6 +18,7 @@ use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\UUIDGenerator;
use Stancl\Tenancy\Exceptions\TenancyNotInitializedException;
test('created event is dispatched', function () {
Event::fake([TenantCreated::class]);
@ -141,6 +142,31 @@ test('a command can be run on a collection of tenants', function () {
expect(Tenant::find('t2')->foo)->toBe('xyz');
});
test('the current method returns the currently initialized tenant', function() {
tenancy()->initialize($tenant = Tenant::create());
expect(Tenant::current())->toBe($tenant);
});
test('the current method returns null if there is no currently initialized tenant', function() {
tenancy()->end();
expect(Tenant::current())->toBeNull();
});
test('currentOrFail method returns the currently initialized tenant', function() {
tenancy()->initialize($tenant = Tenant::create());
expect(Tenant::currentOrFail())->toBe($tenant);
});
test('currentOrFail method throws an exception if there is no currently initialized tenant', function() {
tenancy()->end();
expect(fn() => Tenant::currentOrFail())->toThrow(TenancyNotInitializedException::class);
});
class MyTenant extends Tenant
{
protected $table = 'tenants';