Wire Doctrine's default connection to the tenant database resolved from the subdomain for HTTP requests and tenant-scoped Messenger messages while keeping master-only services on the master connection. This removes the production inconsistency where demo data, migrations and tenant commands used the tenant database but the web runtime still read from master.
87 lines
3.1 KiB
PHP
87 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Shared\Infrastructure\Tenant;
|
|
|
|
use App\Shared\Infrastructure\Tenant\TenantConfig;
|
|
use App\Shared\Infrastructure\Tenant\TenantDatabaseRequestSubscriber;
|
|
use App\Shared\Infrastructure\Tenant\TenantDatabaseSwitcher;
|
|
use App\Shared\Infrastructure\Tenant\TenantId;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
|
use Symfony\Component\HttpKernel\KernelEvents;
|
|
|
|
#[CoversClass(TenantDatabaseRequestSubscriber::class)]
|
|
final class TenantDatabaseRequestSubscriberTest extends TestCase
|
|
{
|
|
#[Test]
|
|
public function itUsesTheTenantDatabaseResolvedByTheMiddleware(): void
|
|
{
|
|
$tenantConfig = new TenantConfig(
|
|
tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
|
|
subdomain: 'ecole-alpha',
|
|
databaseUrl: 'postgresql://tenant:secret@db:5432/classeo_tenant_alpha?serverVersion=18&charset=utf8',
|
|
);
|
|
|
|
$databaseSwitcher = $this->createMock(TenantDatabaseSwitcher::class);
|
|
$databaseSwitcher->expects(self::once())
|
|
->method('useTenantDatabase')
|
|
->with($tenantConfig->databaseUrl);
|
|
|
|
$subscriber = new TenantDatabaseRequestSubscriber($databaseSwitcher);
|
|
$request = Request::create('https://ecole-alpha.classeo.local/api/users');
|
|
$request->attributes->set('_tenant', $tenantConfig);
|
|
|
|
$subscriber->onKernelRequest($this->createRequestEvent($request));
|
|
}
|
|
|
|
#[Test]
|
|
public function itFallsBackToTheDefaultDatabaseWhenNoTenantWasResolved(): void
|
|
{
|
|
$databaseSwitcher = $this->createMock(TenantDatabaseSwitcher::class);
|
|
$databaseSwitcher->expects(self::once())
|
|
->method('useDefaultDatabase');
|
|
|
|
$subscriber = new TenantDatabaseRequestSubscriber($databaseSwitcher);
|
|
$request = Request::create('https://classeo.local/api/docs');
|
|
|
|
$subscriber->onKernelRequest($this->createRequestEvent($request));
|
|
}
|
|
|
|
#[Test]
|
|
public function itResetsTheConnectionAfterTheRequest(): void
|
|
{
|
|
$databaseSwitcher = $this->createMock(TenantDatabaseSwitcher::class);
|
|
$databaseSwitcher->expects(self::once())
|
|
->method('useDefaultDatabase');
|
|
|
|
$subscriber = new TenantDatabaseRequestSubscriber($databaseSwitcher);
|
|
$subscriber->onKernelTerminate();
|
|
}
|
|
|
|
#[Test]
|
|
public function itRegistersItselfRightAfterTenantResolution(): void
|
|
{
|
|
$events = TenantDatabaseRequestSubscriber::getSubscribedEvents();
|
|
|
|
self::assertSame(['onKernelRequest', 255], $events[KernelEvents::REQUEST]);
|
|
self::assertSame('onKernelTerminate', $events[KernelEvents::TERMINATE]);
|
|
}
|
|
|
|
private function createRequestEvent(Request $request): RequestEvent
|
|
{
|
|
$kernel = $this->createMock(HttpKernelInterface::class);
|
|
|
|
return new RequestEvent(
|
|
$kernel,
|
|
$request,
|
|
HttpKernelInterface::MAIN_REQUEST,
|
|
);
|
|
}
|
|
}
|