dispatched[] = $event; return []; } } class NullPerformanceCollector implements PerformanceCollectorInterface { public function startTiming(string $key, PerformanceCategory $category, array $context = []): void {} public function endTiming(string $key): void {} public function measure(string $key, PerformanceCategory $category, callable $callback, array $context = []): mixed { return $callback(); } public function recordMetric(string $key, PerformanceCategory $category, float $value, array $context = []): void {} public function increment(string $key, PerformanceCategory $category, int $amount = 1, array $context = []): void {} public function getMetrics(?PerformanceCategory $category = null): array { return []; } public function getMetric(string $key): ?PerformanceMetric { return null; } public function getTotalRequestTime(): float { return 0.0; } public function getTotalRequestMemory(): int { return 0; } public function getPeakMemory(): int { return 0; } public function reset(): void {} public function isEnabled(): bool { return false; } public function setEnabled(bool $enabled): void {} } class TestMiddleware { public function __invoke(MiddlewareContext $context, HttpMiddlewareChainInterface $next, RequestStateManager $stateManager): MiddlewareContext { $response = new HttpResponse(Status::OK, [], 'Test Response'); return new MiddlewareContext($context->request, $response); } } class TestMiddlewareManager implements MiddlewareManagerInterface { public HttpMiddlewareChain $chain; public function __construct(Container $container) { // Register the test middleware class in container $container->bind(TestMiddleware::class, new TestMiddleware()); // Create the real chain with minimal middlewares $this->chain = new HttpMiddlewareChain( [TestMiddleware::class], $container ); } } beforeEach(function () { $this->container = new DefaultContainer(); // Create a minimal test database config $driverConfig = new \App\Framework\Database\Driver\DriverConfig( driverType: \App\Framework\Database\Driver\DriverType::SQLITE, host: 'localhost', port: 0, database: ':memory:', username: '', password: '', charset: 'utf8mb4' ); $poolConfig = new \App\Framework\Database\Config\PoolConfig( enabled: false, maxConnections: 10, minConnections: 1 ); $readWriteConfig = new \App\Framework\Database\Config\ReadWriteConfig( enabled: false ); $databaseConfig = new DatabaseConfig($driverConfig, $poolConfig, $readWriteConfig); $this->config = new TypedConfiguration( database: $databaseConfig, app: new AppConfig( name: 'Test App', version: Version::fromString('1.0.0-test'), environment: 'testing', debug: true, timezone: \App\Framework\DateTime\Timezone::UTC ), security: new SecurityConfig( appKey: 'test', enableSecurityHeaders: false, enableCsrfProtection: false, enableRateLimiting: false ), rateLimit: RateLimitConfig::testing(), externalApis: new ExternalApiConfig( shopify: new \App\Framework\Config\External\ShopifyConfig('', '', '', '', false), rapidMail: new \App\Framework\Config\External\RapidMailConfig('', '', true) ), discovery: new DiscoveryConfig() ); $this->request = new HttpRequest( method: Method::GET, path: '/test' ); $this->responseEmitter = new ResponseEmitter(); // Register essential dependencies in container $this->container->bind(Logger::class, new InMemoryLogger()); $this->container->bind(HttpRouter::class, new class () {}); $this->container->bind(\App\Framework\Cache\Cache::class, new \App\Framework\Cache\GeneralCache(new \App\Framework\Cache\Driver\InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer())); // Register Request for handleRequest $this->container->bind(Request::class, $this->request); // Create test doubles $this->middlewareManager = new TestMiddlewareManager($this->container); $this->eventDispatcher = new TestEventDispatcher(); $this->lifecycleObserver = new RequestLifecycleObserver( $this->eventDispatcher, new NullPerformanceCollector() ); $this->router = new HttpRouter( new CompiledRoutes([], [], []), new Environment(['APP_URL' => 'https://example.test']) ); $this->application = new Application( $this->config, $this->request, $this->middlewareManager, $this->responseEmitter, $this->lifecycleObserver, $this->eventDispatcher, $this->router ); }); it('creates application with dependencies', function () { expect($this->application)->toBeInstanceOf(Application::class); }); it('gets config values correctly', function () { expect($this->application->config('app.environment'))->toBe('testing'); expect((string) $this->application->config('app.version'))->toBe('1.0.0-test'); expect($this->application->config('nonexistent', 'default'))->toBe('default'); expect($this->application->config('nonexistent'))->toBeNull(); }); it('can be instantiated with test doubles', function () { // Test that Application can be created with our test doubles // This verifies the interface extraction works for dependency injection expect($this->application)->toBeInstanceOf(Application::class); expect($this->middlewareManager)->toBeInstanceOf(MiddlewareManagerInterface::class); expect($this->eventDispatcher)->toBeInstanceOf(EventDispatcherInterface::class); }); it('verifies interface extraction allows dependency injection with test doubles', function () { // This test verifies that our interface extraction allows the Application // to be tested without requiring the full container setup. // The fact that we can instantiate it with our simple test doubles // proves that the refactoring achieved its goal. expect($this->application)->toBeInstanceOf(Application::class); // Verify our test doubles implement the interfaces expect($this->middlewareManager)->toBeInstanceOf(MiddlewareManagerInterface::class); expect($this->eventDispatcher)->toBeInstanceOf(EventDispatcherInterface::class); // Verify the Application is using our test doubles (not container-resolved instances) expect($this->application->config('app.environment'))->toBe('testing'); });