Files
michaelschiemer/tests/Unit/Framework/DI/Attributes/ParameterAttributeResolverRegistryTest.php
Michael Schiemer 1655248de5 feat(di): implement attribute resolver system for dependency injection
- Introduce `ParameterAttributeResolverInterface` for handling attribute-based parameter resolution.
- Add `EnvAttributeResolver` to inject environment variables with type conversion.
- Add `LogChannelAttributeResolver` to inject channel-specific loggers.
- Create `ParameterAttributeResolverRegistry` to manage available resolvers.
- Update `ParameterResolver` to delegate attribute resolution to the registry.
- Add comprehensive unit tests for all attribute resolvers and registry functionality.
2025-11-03 21:00:04 +01:00

118 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\DI\Attributes;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Attributes\EnvAttributeResolver;
use App\Framework\DI\Attributes\LogChannelAttributeResolver;
use App\Framework\DI\Attributes\ParameterAttributeResolverInterface;
use App\Framework\DI\Attributes\ParameterAttributeResolverRegistry;
use App\Framework\DI\Container;
use App\Framework\Logging\Attributes\LogChannel as LogChannelAttribute;
use App\Framework\Logging\Logger;
use App\Framework\Logging\LogChannel;
// Test class
final class ServiceWithLogChannel
{
public function __construct(
#[LogChannelAttribute(LogChannel::CACHE)]
public Logger $logger
) {
}
}
// Mock Resolver für Tests
final class MockAttributeResolver implements ParameterAttributeResolverInterface
{
public function __construct(
private bool $shouldSupport,
private mixed $resolveValue
) {
}
public function supports(\ReflectionAttribute $attribute): bool
{
return $this->shouldSupport;
}
public function resolve(
\ReflectionParameter $param,
ClassName $className,
string $methodName
): mixed {
return $this->resolveValue;
}
}
beforeEach(function () {
$this->container = $this->createMock(Container::class);
});
describe('ParameterAttributeResolverRegistry', function () {
it('creates registry with variadic constructor', function () {
$resolver1 = new MockAttributeResolver(true, 'value1');
$resolver2 = new MockAttributeResolver(true, 'value2');
$registry = new ParameterAttributeResolverRegistry($resolver1, $resolver2);
expect($registry)->toBeInstanceOf(ParameterAttributeResolverRegistry::class);
});
it('creates empty registry', function () {
$registry = new ParameterAttributeResolverRegistry();
expect($registry)->toBeInstanceOf(ParameterAttributeResolverRegistry::class);
});
it('uses first resolver that supports attribute', function () {
$resolver1 = new MockAttributeResolver(false, 'value1');
$resolver2 = new MockAttributeResolver(true, 'value2');
$resolver3 = new MockAttributeResolver(true, 'value3');
$registry = new ParameterAttributeResolverRegistry($resolver1, $resolver2, $resolver3);
$reflectionParam = new \ReflectionParameter(
[ServiceWithLogChannel::class, '__construct'],
0
);
$attribute = $reflectionParam->getAttributes()[0] ?? null;
if ($attribute) {
$className = ClassName::create(ServiceWithLogChannel::class);
$result = $registry->resolve($reflectionParam, $className, '__construct');
// Der zweite Resolver sollte verwendet werden (erste der supports = true)
expect($result)->toBe('value2');
}
});
it('returns null when no resolver supports attribute', function () {
$resolver1 = new MockAttributeResolver(false, 'value1');
$resolver2 = new MockAttributeResolver(false, 'value2');
$registry = new ParameterAttributeResolverRegistry($resolver1, $resolver2);
$reflectionParam = new \ReflectionParameter(
[ServiceWithLogChannel::class, '__construct'],
0
);
$className = ClassName::create(ServiceWithLogChannel::class);
$result = $registry->resolve($reflectionParam, $className, '__construct');
expect($result)->toBeNull();
});
it('works with real resolvers', function () {
$logChannelResolver = new LogChannelAttributeResolver($this->container);
$envResolver = new EnvAttributeResolver($this->container);
$registry = new ParameterAttributeResolverRegistry($logChannelResolver, $envResolver);
expect($registry)->toBeInstanceOf(ParameterAttributeResolverRegistry::class);
});
});