resolver = $this->createMock(TenantResolver::class); $this->context = new TenantContext(); } #[Test] public function itSetsTenantContextForValidTenant(): void { $tenantId = TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'); $config = new TenantConfig( tenantId: $tenantId, subdomain: 'ecole-alpha', databaseUrl: 'postgresql://user:pass@localhost:5432/classeo_alpha', ); $this->resolver->method('resolve') ->with('ecole-alpha.classeo.local') ->willReturn($config); $middleware = new TenantMiddleware($this->resolver, $this->context); $request = Request::create('https://ecole-alpha.classeo.local/api/test'); $event = $this->createRequestEvent($request); $middleware->onKernelRequest($event); self::assertTrue($this->context->hasTenant()); self::assertTrue($tenantId->equals($this->context->getCurrentTenantId())); } #[Test] public function itReturns404ForNonExistentTenant(): void { $this->resolver->method('resolve') ->with('ecole-inexistant.classeo.local') ->willThrowException(TenantNotFoundException::withSubdomain('ecole-inexistant')); $middleware = new TenantMiddleware($this->resolver, $this->context); $request = Request::create('https://ecole-inexistant.classeo.local/api/test'); $event = $this->createRequestEvent($request); $middleware->onKernelRequest($event); self::assertTrue($event->hasResponse()); self::assertSame(Response::HTTP_NOT_FOUND, $event->getResponse()?->getStatusCode()); } #[Test] public function itReturnsGenericErrorMessageFor404(): void { $this->resolver->method('resolve') ->willThrowException(TenantNotFoundException::withSubdomain('test')); $middleware = new TenantMiddleware($this->resolver, $this->context); $request = Request::create('https://test.classeo.local/api/test'); $event = $this->createRequestEvent($request); $middleware->onKernelRequest($event); $response = $event->getResponse(); self::assertNotNull($response); $content = json_decode((string) $response->getContent(), true); self::assertSame('Resource not found', $content['message']); self::assertArrayNotHasKey('subdomain', $content); } #[Test] public function itClearsTenantContextOnTerminate(): void { $config = new TenantConfig( tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'), subdomain: 'ecole-alpha', databaseUrl: 'postgresql://user:pass@localhost:5432/classeo_alpha', ); $this->context->setCurrentTenant($config); self::assertTrue($this->context->hasTenant()); $middleware = new TenantMiddleware($this->resolver, $this->context); $middleware->onKernelTerminate(); self::assertFalse($this->context->hasTenant()); } #[Test] public function itRegistersCorrectEvents(): void { $events = TenantMiddleware::getSubscribedEvents(); self::assertArrayHasKey(KernelEvents::REQUEST, $events); self::assertArrayHasKey(KernelEvents::TERMINATE, $events); // Request listener should have high priority to run early $requestConfig = $events[KernelEvents::REQUEST]; self::assertIsArray($requestConfig); self::assertSame('onKernelRequest', $requestConfig[0]); self::assertGreaterThan(0, $requestConfig[1]); // High priority } private function createRequestEvent(Request $request): RequestEvent { $kernel = $this->createMock(HttpKernelInterface::class); return new RequestEvent( $kernel, $request, HttpKernelInterface::MAIN_REQUEST ); } }