Lorsqu'un super-admin crée un établissement via l'interface, le système doit automatiquement créer la base tenant, exécuter les migrations, créer le premier utilisateur admin et envoyer l'invitation — le tout de manière asynchrone pour ne pas bloquer la réponse HTTP. Ce mécanisme rend chaque établissement opérationnel dès sa création sans intervention manuelle sur l'infrastructure.
120 lines
4.1 KiB
PHP
120 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Shared\Infrastructure\Tenant;
|
|
|
|
use App\Shared\Infrastructure\Tenant\DoctrineTenantRegistry;
|
|
use App\Shared\Infrastructure\Tenant\TenantId;
|
|
use App\Shared\Infrastructure\Tenant\TenantNotFoundException;
|
|
use Doctrine\DBAL\Connection;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
#[CoversClass(DoctrineTenantRegistry::class)]
|
|
final class DoctrineTenantRegistryTest extends TestCase
|
|
{
|
|
private const string MASTER_URL = 'postgresql://classeo:secret@db:5432/classeo_master';
|
|
private const string TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
|
|
private const string SUBDOMAIN = 'ecole-alpha';
|
|
private const string DB_NAME = 'classeo_tenant_a1b2c3d4e5f67890abcdef1234567890';
|
|
|
|
#[Test]
|
|
public function itResolvesConfigBySubdomain(): void
|
|
{
|
|
$registry = $this->registryWith([
|
|
['tenant_id' => self::TENANT_ID, 'subdomain' => self::SUBDOMAIN, 'database_name' => self::DB_NAME],
|
|
]);
|
|
|
|
$config = $registry->getBySubdomain(self::SUBDOMAIN);
|
|
|
|
self::assertSame(self::SUBDOMAIN, $config->subdomain);
|
|
self::assertSame(self::TENANT_ID, (string) $config->tenantId);
|
|
self::assertSame('postgresql://classeo:secret@db:5432/' . self::DB_NAME, $config->databaseUrl);
|
|
}
|
|
|
|
#[Test]
|
|
public function itResolvesConfigByTenantId(): void
|
|
{
|
|
$registry = $this->registryWith([
|
|
['tenant_id' => self::TENANT_ID, 'subdomain' => self::SUBDOMAIN, 'database_name' => self::DB_NAME],
|
|
]);
|
|
|
|
$config = $registry->getConfig(TenantId::fromString(self::TENANT_ID));
|
|
|
|
self::assertSame(self::SUBDOMAIN, $config->subdomain);
|
|
}
|
|
|
|
#[Test]
|
|
public function itThrowsForUnknownSubdomain(): void
|
|
{
|
|
$registry = $this->registryWith([]);
|
|
|
|
$this->expectException(TenantNotFoundException::class);
|
|
$registry->getBySubdomain('inexistant');
|
|
}
|
|
|
|
#[Test]
|
|
public function itThrowsForUnknownTenantId(): void
|
|
{
|
|
$registry = $this->registryWith([]);
|
|
|
|
$this->expectException(TenantNotFoundException::class);
|
|
$registry->getConfig(TenantId::fromString(self::TENANT_ID));
|
|
}
|
|
|
|
#[Test]
|
|
public function itChecksExistence(): void
|
|
{
|
|
$registry = $this->registryWith([
|
|
['tenant_id' => self::TENANT_ID, 'subdomain' => self::SUBDOMAIN, 'database_name' => self::DB_NAME],
|
|
]);
|
|
|
|
self::assertTrue($registry->exists(self::SUBDOMAIN));
|
|
self::assertFalse($registry->exists('inexistant'));
|
|
}
|
|
|
|
#[Test]
|
|
public function itReturnsAllConfigs(): void
|
|
{
|
|
$registry = $this->registryWith([
|
|
['tenant_id' => self::TENANT_ID, 'subdomain' => self::SUBDOMAIN, 'database_name' => self::DB_NAME],
|
|
['tenant_id' => 'b2c3d4e5-f6a7-8901-bcde-f12345678901', 'subdomain' => 'ecole-beta', 'database_name' => 'classeo_tenant_beta'],
|
|
]);
|
|
|
|
$configs = $registry->getAllConfigs();
|
|
|
|
self::assertCount(2, $configs);
|
|
}
|
|
|
|
#[Test]
|
|
public function itQueriesDatabaseOnlyOnce(): void
|
|
{
|
|
$connection = $this->createMock(Connection::class);
|
|
$connection->expects(self::once())
|
|
->method('fetchAllAssociative')
|
|
->willReturn([
|
|
['tenant_id' => self::TENANT_ID, 'subdomain' => self::SUBDOMAIN, 'database_name' => self::DB_NAME],
|
|
]);
|
|
|
|
$registry = new DoctrineTenantRegistry($connection, self::MASTER_URL);
|
|
|
|
$registry->getBySubdomain(self::SUBDOMAIN);
|
|
$registry->getConfig(TenantId::fromString(self::TENANT_ID));
|
|
$registry->exists(self::SUBDOMAIN);
|
|
$registry->getAllConfigs();
|
|
}
|
|
|
|
/**
|
|
* @param array<array{tenant_id: string, subdomain: string, database_name: string}> $rows
|
|
*/
|
|
private function registryWith(array $rows): DoctrineTenantRegistry
|
|
{
|
|
$connection = $this->createMock(Connection::class);
|
|
$connection->method('fetchAllAssociative')->willReturn($rows);
|
|
|
|
return new DoctrineTenantRegistry($connection, self::MASTER_URL);
|
|
}
|
|
}
|