- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
221 lines
7.3 KiB
PHP
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);
|
|
}
|
|
}
|