fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled

This commit is contained in:
2025-11-24 21:28:25 +01:00
parent 4eb7134853
commit 77abc65cd7
1327 changed files with 91915 additions and 9909 deletions

View File

@@ -0,0 +1,348 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\CommandBus;
use App\Framework\Attributes\AfterExecute;
use App\Framework\Attributes\BeforeExecute;
use App\Framework\Attributes\Execution\AttributeExecutionInitializer;
use App\Framework\Attributes\Execution\AttributeRunner;
use App\Framework\Attributes\Execution\CallbackExecutor;
use App\Framework\Attributes\Execution\HandlerAttributeExecutor;
use App\Framework\Attributes\OnError;
use App\Framework\CommandBus\CommandBus;
use App\Framework\CommandBus\CommandHandler;
use App\Framework\CommandBus\CommandHandlersCollection;
use App\Framework\CommandBus\CommandHandlerDescriptor;
use App\Framework\CommandBus\DefaultCommandBus;
use App\Framework\Context\ContextType;
use App\Framework\Context\ExecutionContext;
use App\Framework\DI\Container;
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\MethodInvoker;
use App\Framework\Discovery\Results\AttributeRegistry;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\ValueObjects\AttributeTarget;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
use App\Framework\Logging\Logger;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\PerformanceMetric;
use App\Framework\Queue\Queue;
use App\Framework\Reflection\SimpleReflectionService;
use PHPUnit\Framework\TestCase;
final class AttributeExecutionIntegrationTest extends TestCase
{
private Container $container;
private CommandBus $commandBus;
private \ArrayObject $beforeExecuted;
private \ArrayObject $afterExecuted;
private \ArrayObject $onErrorExecuted;
protected function setUp(): void
{
$this->container = new DefaultContainer();
$this->container->instance(MethodInvoker::class, new MethodInvoker(
$this->container,
new SimpleReflectionService()
));
// Mock Logger
$logger = $this->createMock(Logger::class);
$this->container->instance(Logger::class, $logger);
// Mock Queue
$queue = $this->createMock(Queue::class);
$this->container->instance(Queue::class, $queue);
// Mock PerformanceCollectorInterface (optional - wird nur verwendet wenn verfügbar)
// Wir registrieren es nicht, damit die Middleware es als optional behandelt
// Mock ExecutionContext
$executionContext = new ExecutionContext(ContextType::TEST);
$this->container->instance(ExecutionContext::class, $executionContext);
// Reset execution tracking
$this->beforeExecuted = new \ArrayObject();
$this->afterExecuted = new \ArrayObject();
$this->onErrorExecuted = new \ArrayObject();
}
public function testBeforeExecuteAttributeIsExecuted(): void
{
$command = new TestCommand('test');
$handler = new TestHandlerWithBeforeAttribute($this->beforeExecuted);
$this->setupCommandBus($handler::class, 'handle', TestCommand::class);
$this->commandBus->dispatch($command);
$this->assertContains('before', (array)$this->beforeExecuted, 'BeforeExecute should have been executed');
}
public function testAfterExecuteAttributeIsExecuted(): void
{
$command = new TestCommand('test');
$handler = new TestHandlerWithAfterAttribute($this->afterExecuted);
$this->setupCommandBus($handler::class, 'handle', TestCommand::class);
$result = $this->commandBus->dispatch($command);
$this->assertContains('after', (array)$this->afterExecuted);
$this->assertEquals('result', $result);
}
public function testOnErrorAttributeIsExecuted(): void
{
$command = new TestCommand('test');
$handler = new TestHandlerWithOnErrorAttribute($this->onErrorExecuted);
$this->setupCommandBus($handler::class, 'handle', TestCommand::class);
try {
$this->commandBus->dispatch($command);
$this->fail('Expected exception was not thrown');
} catch (\RuntimeException $e) {
$this->assertEquals('Test error', $e->getMessage());
}
$this->assertContains('error', (array)$this->onErrorExecuted);
}
private function setupCommandBus(string $handlerClass, string $handlerMethod, string $commandClass): void
{
// Erstelle Handler-Descriptor
$handlerDescriptor = new CommandHandlerDescriptor(
class: $handlerClass,
method: $handlerMethod,
command: $commandClass
);
$handlersCollection = new CommandHandlersCollection($handlerDescriptor);
$this->container->instance(CommandHandlersCollection::class, $handlersCollection);
// Erstelle DiscoveryRegistry mit Handler-Attribut
$discoveryRegistry = new DiscoveryRegistry(
attributes: new AttributeRegistry()
);
// Füge CommandHandler-Attribut hinzu
$commandHandlerAttribute = new DiscoveredAttribute(
className: \App\Framework\Core\ValueObjects\ClassName::create($handlerClass),
attributeClass: CommandHandler::class,
target: AttributeTarget::METHOD,
methodName: \App\Framework\Core\ValueObjects\MethodName::create($handlerMethod),
arguments: [],
additionalData: [
'class' => $handlerClass,
'method' => $handlerMethod,
'command' => $commandClass,
]
);
$discoveryRegistry->attributes->add(CommandHandler::class, $commandHandlerAttribute);
// Entdecke BeforeExecute, AfterExecute, OnError Attribute für den Handler
$reflectionClass = new \ReflectionClass($handlerClass);
$reflectionMethod = $reflectionClass->getMethod($handlerMethod);
foreach ($reflectionMethod->getAttributes(BeforeExecute::class) as $attribute) {
$beforeAttribute = new DiscoveredAttribute(
className: \App\Framework\Core\ValueObjects\ClassName::create($handlerClass),
attributeClass: BeforeExecute::class,
target: AttributeTarget::METHOD,
methodName: \App\Framework\Core\ValueObjects\MethodName::create($handlerMethod),
arguments: $attribute->getArguments(),
additionalData: []
);
$discoveryRegistry->attributes->add(BeforeExecute::class, $beforeAttribute);
}
foreach ($reflectionMethod->getAttributes(AfterExecute::class) as $attribute) {
$afterAttribute = new DiscoveredAttribute(
className: \App\Framework\Core\ValueObjects\ClassName::create($handlerClass),
attributeClass: AfterExecute::class,
target: AttributeTarget::METHOD,
methodName: \App\Framework\Core\ValueObjects\MethodName::create($handlerMethod),
arguments: $attribute->getArguments(),
additionalData: []
);
$discoveryRegistry->attributes->add(AfterExecute::class, $afterAttribute);
}
foreach ($reflectionMethod->getAttributes(OnError::class) as $attribute) {
$onErrorAttribute = new DiscoveredAttribute(
className: \App\Framework\Core\ValueObjects\ClassName::create($handlerClass),
attributeClass: OnError::class,
target: AttributeTarget::METHOD,
methodName: \App\Framework\Core\ValueObjects\MethodName::create($handlerMethod),
arguments: $attribute->getArguments(),
additionalData: []
);
$discoveryRegistry->attributes->add(OnError::class, $onErrorAttribute);
}
$this->container->instance(\App\Framework\Discovery\Results\DiscoveryRegistry::class, $discoveryRegistry);
// Initialisiere Attribute Execution System
$callbackExecutor = new CallbackExecutor($this->container);
$this->container->instance(CallbackExecutor::class, $callbackExecutor);
$attributeRunner = new AttributeRunner(
discoveryRegistry: $discoveryRegistry,
container: $this->container,
callbackExecutor: $callbackExecutor
);
$this->container->instance(AttributeRunner::class, $attributeRunner);
// Registriere Test-Handler im Container
$handlerInstance = new $handlerClass(...$this->getHandlerDependencies($handlerClass));
$this->container->instance($handlerClass, $handlerInstance);
// Registriere Test-Attribute-Handler mit korrekten Referenzen
// WICHTIG: Wir müssen die ArrayObjects direkt übergeben, nicht über Closures
if (str_contains($handlerClass, 'Before')) {
$this->container->instance(TestBeforeHandler::class, new TestBeforeHandler($this->beforeExecuted));
}
if (str_contains($handlerClass, 'After')) {
$this->container->instance(TestAfterHandler::class, new TestAfterHandler($this->afterExecuted));
}
if (str_contains($handlerClass, 'OnError')) {
$this->container->instance(TestOnErrorHandler::class, new TestOnErrorHandler($this->onErrorExecuted));
}
$handlerAttributeExecutor = new HandlerAttributeExecutor(
discoveryRegistry: $discoveryRegistry,
container: $this->container,
attributeRunner: $attributeRunner
);
$this->container->instance(HandlerAttributeExecutor::class, $handlerAttributeExecutor);
// Erstelle CommandBus
$executionContext = $this->container->get(ExecutionContext::class);
$queue = $this->container->get(Queue::class);
$logger = $this->container->get(Logger::class);
$this->commandBus = new DefaultCommandBus(
commandHandlers: $handlersCollection,
container: $this->container,
executionContext: $executionContext,
queue: $queue,
logger: $logger
);
}
/**
* @return array<mixed>
*/
private function getHandlerDependencies(string $handlerClass): array
{
if (str_contains($handlerClass, 'Before')) {
return [$this->beforeExecuted];
}
if (str_contains($handlerClass, 'After')) {
return [$this->afterExecuted];
}
if (str_contains($handlerClass, 'OnError')) {
return [$this->onErrorExecuted];
}
return [];
}
}
// Test Command
final readonly class TestCommand
{
public function __construct(
public string $data
) {
}
}
// Test Handler mit BeforeExecute Attribute
final class TestHandlerWithBeforeAttribute
{
public function __construct(
private \ArrayObject $beforeExecuted
) {}
#[BeforeExecute(TestBeforeHandler::class)]
#[CommandHandler]
public function handle(TestCommand $command): void
{
// Handler logic
}
}
// Test Handler mit AfterExecute Attribute
final class TestHandlerWithAfterAttribute
{
public function __construct(
private \ArrayObject $afterExecuted
) {}
#[AfterExecute(TestAfterHandler::class)]
#[CommandHandler]
public function handle(TestCommand $command): string
{
return 'result';
}
}
// Test Handler mit OnError Attribute
final class TestHandlerWithOnErrorAttribute
{
public function __construct(
private \ArrayObject $onErrorExecuted
) {}
#[OnError(TestOnErrorHandler::class)]
#[CommandHandler]
public function handle(TestCommand $command): void
{
throw new \RuntimeException('Test error');
}
}
// Test Handler-Klassen für Attribute
final class TestBeforeHandler
{
public function __construct(
private \ArrayObject $beforeExecuted
) {}
public function __invoke($ctx): void
{
$this->beforeExecuted[] = 'before';
}
}
final class TestAfterHandler
{
public function __construct(
private \ArrayObject $afterExecuted
) {}
public function __invoke($ctx): void
{
$this->afterExecuted[] = 'after';
}
}
final class TestOnErrorHandler
{
public function __construct(
private \ArrayObject $onErrorExecuted
) {}
public function __invoke($ctx): void
{
$this->onErrorExecuted[] = 'error';
}
}