Files
michaelschiemer/tests/Framework/Http/MiddlewareDependencyResolverTest.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

221 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Http;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Container;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewareDependencyResolver;
use App\Framework\Http\Next;
use App\Framework\Http\RequestStateManager;
use App\Framework\Logging\Logger;
use App\Framework\Reflection\ReflectionProvider;
use PHPUnit\Framework\TestCase;
final class MiddlewareDependencyResolverTest extends TestCase
{
private MiddlewareDependencyResolver $resolver;
private Container $container;
private ReflectionProvider $reflectionProvider;
private Logger $logger;
protected function setUp(): void
{
$this->container = $this->createMock(Container::class);
$this->reflectionProvider = $this->createMock(ReflectionProvider::class);
$this->logger = $this->createMock(Logger::class);
$this->resolver = new MiddlewareDependencyResolver(
$this->reflectionProvider,
$this->container,
$this->logger
);
}
public function test_resolves_simple_middleware_without_dependencies(): void
{
// Create a simple middleware class for testing
$middlewareClass = SimpleTestMiddleware::class;
// Mock that the class exists and is instantiable
$this->reflectionProvider
->method('isInstantiable')
->willReturn(true);
// Mock that it has no constructor parameters
$this->reflectionProvider
->method('getMethodParameters')
->willReturn([]);
// Mock that it implements HttpMiddleware
$this->reflectionProvider
->method('implementsInterface')
->with(
$this->callback(fn ($className) => $className instanceof ClassName),
HttpMiddleware::class
)
->willReturn(true);
$result = $this->resolver->resolve([$middlewareClass]);
$this->assertCount(1, $result->getMiddlewares());
$this->assertContains($middlewareClass, $result->getMiddlewares());
}
public function test_filters_out_middleware_with_missing_dependencies(): void
{
$middlewareClass = MiddlewareWithDependencies::class;
// Mock that the class exists and is instantiable
$this->reflectionProvider
->method('isInstantiable')
->willReturn(true);
// Mock constructor parameter that requires a dependency
$parameterMock = $this->createMock(\ReflectionParameter::class);
$typeMock = $this->createMock(\ReflectionType::class);
$parameterMock->method('getType')->willReturn($typeMock);
$parameterMock->method('isOptional')->willReturn(false);
$parameterMock->method('allowsNull')->willReturn(false);
$typeMock->method('isBuiltin')->willReturn(false);
$typeMock->method('getName')->willReturn('SomeService');
$this->reflectionProvider
->method('getMethodParameters')
->willReturn([$parameterMock]);
// Mock that the dependency is NOT available in container
$this->container
->method('has')
->with('SomeService')
->willReturn(false);
// Mock that it implements HttpMiddleware
$this->reflectionProvider
->method('implementsInterface')
->willReturn(true);
// Expect warning to be logged
$this->logger
->expects($this->once())
->method('warning')
->with($this->stringContains('Missing dependencies for MiddlewareWithDependencies: SomeService'));
$result = $this->resolver->resolve([$middlewareClass]);
// Should be filtered out due to missing dependency
$this->assertCount(0, $result->getMiddlewares());
}
public function test_includes_middleware_with_available_dependencies(): void
{
$middlewareClass = MiddlewareWithDependencies::class;
// Mock that the class exists and is instantiable
$this->reflectionProvider
->method('isInstantiable')
->willReturn(true);
// Mock constructor parameter that requires a dependency
$parameterMock = $this->createMock(\ReflectionParameter::class);
$typeMock = $this->createMock(\ReflectionType::class);
$parameterMock->method('getType')->willReturn($typeMock);
$parameterMock->method('isOptional')->willReturn(false);
$parameterMock->method('allowsNull')->willReturn(false);
$typeMock->method('isBuiltin')->willReturn(false);
$typeMock->method('getName')->willReturn('SomeService');
$this->reflectionProvider
->method('getMethodParameters')
->willReturn([$parameterMock]);
// Mock that the dependency IS available in container
$this->container
->method('has')
->with('SomeService')
->willReturn(true);
// Mock that it implements HttpMiddleware
$this->reflectionProvider
->method('implementsInterface')
->willReturn(true);
$result = $this->resolver->resolve([$middlewareClass]);
// Should be included because dependency is available
$this->assertCount(1, $result->getMiddlewares());
$this->assertContains($middlewareClass, $result->getMiddlewares());
}
public function test_logs_information_about_resolution_process(): void
{
$middlewareClass = SimpleTestMiddleware::class;
// Mock successful resolution
$this->reflectionProvider->method('isInstantiable')->willReturn(true);
$this->reflectionProvider->method('getMethodParameters')->willReturn([]);
$this->reflectionProvider->method('implementsInterface')->willReturn(true);
// Expect debug and info logs
$this->logger
->expects($this->once())
->method('debug')
->with($this->stringContains('Starting resolution for 1 middlewares'));
$this->logger
->expects($this->once())
->method('info')
->with($this->stringContains('Resolution completed with 1 middlewares'));
$this->resolver->resolve([$middlewareClass]);
}
public function test_handles_non_existent_middleware_class(): void
{
$nonExistentClass = 'NonExistentMiddleware';
// Expect warning to be logged
$this->logger
->expects($this->once())
->method('warning')
->with($this->stringContains('Class not found: NonExistentMiddleware'));
$result = $this->resolver->resolve([$nonExistentClass]);
// Should return empty result
$this->assertCount(0, $result->getMiddlewares());
}
}
// Test middleware classes
final class SimpleTestMiddleware implements HttpMiddleware
{
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
return $next($context);
}
}
final class MiddlewareWithDependencies implements HttpMiddleware
{
public function __construct(
private object $someService
) {
}
public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
{
return $next($context);
}
}