diff --git a/.gitattributes b/.gitattributes index 862e7288..1bcf0d69 100644 --- a/.gitattributes +++ b/.gitattributes @@ -34,7 +34,10 @@ /psalm.xml export-ignore /phpunit.xml.dist export-ignore /Makefile export-ignore -/tests export-ignore +/tests/App export-ignore +/tests/Integration export-ignore +/tests/Unit export-ignore +/tests/runtime export-ignore /docs export-ignore # Avoid merge conflicts in CHANGELOG diff --git a/.gitignore b/.gitignore index 9b3a5cd5..5c9b52f1 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ composer.lock # PHPUnit .phpunit.result.cache +.phpunit.cache coverage.html diff --git a/README.md b/README.md index 5755c3ff..472ddb56 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ $data = [ 'url' => $url, 'destinationFile' => $filename, ]; -$message = new \Yiisoft\Queue\Message\Message('file-download', $data); +$message = new \Yiisoft\Queue\Message\Message(FileDownloader::class, $data); ``` Then you should push it to the queue: @@ -80,7 +80,10 @@ $queue->push($message); Its handler may look like the following: ```php -class FileDownloader +use Yiisoft\Queue\Message\MessageInterface; +use Yiisoft\Queue\Message\MessageHandlerInterface; + +class FileDownloader implements MessageHandlerInterface { private string $absolutePath; @@ -89,11 +92,13 @@ class FileDownloader $this->absolutePath = $absolutePath; } - public function handle(\Yiisoft\Queue\Message\MessageInterface $downloadMessage): void + public function handle(\Yiisoft\Queue\Message\MessageInterface $downloadMessage): MessageInterface { - $fileName = $downloadMessage->getData()['destinationFile']; + $fileName = $message->getData()['destinationFile']; $path = "$this->absolutePath/$fileName"; - file_put_contents($path, file_get_contents($downloadMessage->getData()['url'])); + file_put_contents($path, file_get_contents($message->getData()['url'])); + + return $message; } } ``` @@ -101,9 +106,7 @@ class FileDownloader The last thing we should do is to create a configuration for the `Yiisoft\Queue\Worker\Worker`: ```php -$handlers = ['file-download' => [new FileDownloader('/path/to/save/files'), 'handle']]; $worker = new \Yiisoft\Queue\Worker\Worker( - $handlers, // Here it is $logger, $injector, $container diff --git a/composer.json b/composer.json index 69d46812..aa98f578 100644 --- a/composer.json +++ b/composer.json @@ -28,11 +28,15 @@ "require": { "php": "^8.1", "psr/container": "^1.0|^2.0", + "psr/event-dispatcher": "^1.0", "psr/log": "^2.0|^3.0", "symfony/console": "^5.4|^6.0", "yiisoft/definitions": "^1.0|^2.0|^3.0", + "yiisoft/event-dispatcher": "^1.1", "yiisoft/friendly-exception": "^1.0", - "yiisoft/injector": "^1.0" + "yiisoft/injector": "^1.0", + "yiisoft/var-dumper": "^1.7", + "yiisoft/yii-event": "^2.1" }, "require-dev": { "maglnet/composer-require-checker": "^4.7", @@ -49,7 +53,8 @@ }, "autoload": { "psr-4": { - "Yiisoft\\Queue\\": "src" + "Yiisoft\\Queue\\": "src", + "Yiisoft\\Queue\\Tests\\Shared\\": "tests/Shared" } }, "autoload-dev": { @@ -66,6 +71,8 @@ }, "config-plugin": { "di": "di.php", + "events-web": "events-web.php", + "events-console": "events-console.php", "params": "params.php" } }, diff --git a/config/di.php b/config/di.php index 3af1ad8b..10b68fc6 100644 --- a/config/di.php +++ b/config/di.php @@ -3,60 +3,77 @@ declare(strict_types=1); use Psr\Container\ContainerInterface; +use Yiisoft\Config\ConfigInterface; +use Yiisoft\Definitions\Reference; +use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; +use Yiisoft\EventDispatcher\Provider\Provider; +use Yiisoft\Injector\Injector; +use Yiisoft\Queue\Adapter\AdapterInterface; +use Yiisoft\Queue\Adapter\SynchronousAdapter; use Yiisoft\Queue\Cli\LoopInterface; use Yiisoft\Queue\Cli\SignalLoop; use Yiisoft\Queue\Cli\SimpleLoop; -use Yiisoft\Queue\Command\ListenAllCommand; -use Yiisoft\Queue\Command\RunCommand; use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Message\MessageSerializerInterface; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsume; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsumeInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailure; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPush; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPushInterface; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; +use Yiisoft\Queue\Command\ListenAllCommand; +use Yiisoft\Queue\Command\RunCommand; use Yiisoft\Queue\Queue; use Yiisoft\Queue\QueueFactory; use Yiisoft\Queue\QueueFactoryInterface; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Worker\Worker as QueueWorker; use Yiisoft\Queue\Worker\WorkerInterface; +use Yiisoft\Yii\Event\ListenerCollectionFactory; /* @var array $params */ return [ QueueWorker::class => [ 'class' => QueueWorker::class, - '__construct()' => [$params['yiisoft/queue']['handlers']], + '__construct()' => [ + 'eventDispatcher' => Reference::to('queue.dispatcher'), + ], ], WorkerInterface::class => QueueWorker::class, LoopInterface::class => static function (ContainerInterface $container): LoopInterface { - return extension_loaded('pcntl') - ? $container->get(SignalLoop::class) - : $container->get(SimpleLoop::class); + return $container->get( + extension_loaded('pcntl') + ? SignalLoop::class + : SimpleLoop::class + ); + }, + 'queue.middlewareDispatcher.push' => static function (Injector $injector) use ($params) { + return $injector->make( + MiddlewareDispatcher::class, + ['middlewareDefinitions' => $params['yiisoft/queue']['middlewares-push']] + ); }, + Queue::class => [ + '__construct()' => [ + 'adapter' => Reference::to(AdapterInterface::class), + 'pushMiddlewareDispatcher' => Reference::to('queue.middlewareDispatcher.push'), + ], + ], QueueFactoryInterface::class => QueueFactory::class, QueueFactory::class => [ '__construct()' => ['channelConfiguration' => $params['yiisoft/queue']['channel-definitions']], ], + AdapterInterface::class => SynchronousAdapter::class, + QueueInterface::class => Queue::class, - MiddlewareFactoryPushInterface::class => MiddlewareFactoryPush::class, - MiddlewareFactoryConsumeInterface::class => MiddlewareFactoryConsume::class, - MiddlewareFactoryFailureInterface::class => MiddlewareFactoryFailure::class, - PushMiddlewareDispatcher::class => [ - '__construct()' => ['middlewareDefinitions' => $params['yiisoft/queue']['middlewares-push']], - ], - ConsumeMiddlewareDispatcher::class => [ - '__construct()' => ['middlewareDefinitions' => $params['yiisoft/queue']['middlewares-consume']], - ], - FailureMiddlewareDispatcher::class => [ - '__construct()' => ['middlewareDefinitions' => $params['yiisoft/queue']['middlewares-fail']], - ], MessageSerializerInterface::class => JsonMessageSerializer::class, + MiddlewareFactoryInterface::class => MiddlewareFactory::class, + + 'queue.dispatcher' => static function (ConfigInterface $config, ListenerCollectionFactory $factory) { + $listeners = $factory->create($config->get('queue')); + + $provider = new Provider($listeners); + + return new Dispatcher($provider); + }, RunCommand::class => [ '__construct()' => [ 'channels' => array_keys($params['yiisoft/queue']['channel-definitions']), diff --git a/config/events-console.php b/config/events-console.php new file mode 100644 index 00000000..bc9e6c71 --- /dev/null +++ b/config/events-console.php @@ -0,0 +1,20 @@ + [ + function (AdapterInterface $adapter, ContainerInterface $container) { + if ($adapter instanceof SynchronousAdapter) { + $container->get(QueueInterface::class)->run(0); + } + }, + ], +]; diff --git a/config/events-web.php b/config/events-web.php new file mode 100644 index 00000000..a21ad205 --- /dev/null +++ b/config/events-web.php @@ -0,0 +1,20 @@ + [ + function (AdapterInterface $adapter, ContainerInterface $container) { + if ($adapter instanceof SynchronousAdapter) { + $container->get(QueueInterface::class)->run(0); + } + }, + ], +]; diff --git a/config/params.php b/config/params.php index 1251c408..0ff99fd7 100644 --- a/config/params.php +++ b/config/params.php @@ -21,7 +21,6 @@ ], ], 'yiisoft/queue' => [ - 'handlers' => [], 'channel-definitions' => [ QueueFactoryInterface::DEFAULT_CHANNEL_NAME => AdapterInterface::class, ], diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 00000000..3613a8b0 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,7 @@ +runExisting(function (MessageInterface $message): bool { - $this->worker->process($message, $this->queue); - - return true; - }); - } - public function runExisting(callable $handlerCallback): void { $result = true; - while (isset($this->messages[$this->current]) && $result === true) { - $result = $handlerCallback($this->messages[$this->current]); + while ($result === true && isset($this->messages[$this->current])) { + $result = $handlerCallback( + $this->messageSerializer->unserialize($this->messages[$this->current]) + ); unset($this->messages[$this->current]); $this->current++; } @@ -65,9 +56,10 @@ public function status(string|int $id): JobStatus public function push(MessageInterface $message): MessageInterface { $key = count($this->messages) + $this->current; - $this->messages[] = $message; + $newMessage = new IdEnvelope($message, $key); + $this->messages[] = $this->messageSerializer->serialize($newMessage); - return new IdEnvelope($message, $key); + return $newMessage; } public function subscribe(callable $handlerCallback): void diff --git a/src/Debug/QueueCollector.php b/src/Debug/QueueCollector.php index 1a8c9d76..d0bc7171 100644 --- a/src/Debug/QueueCollector.php +++ b/src/Debug/QueueCollector.php @@ -4,11 +4,11 @@ namespace Yiisoft\Queue\Debug; +use Yiisoft\Queue\Middleware\MiddlewareInterface; use Yiisoft\Yii\Debug\Collector\CollectorTrait; use Yiisoft\Yii\Debug\Collector\SummaryCollectorInterface; use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; use Yiisoft\Queue\QueueInterface; final class QueueCollector implements SummaryCollectorInterface @@ -53,7 +53,7 @@ public function collectStatus(string $id, JobStatus $status): void public function collectPush( string $channel, MessageInterface $message, - string|array|callable|MiddlewarePushInterface ...$middlewareDefinitions, + string|array|callable|MiddlewareInterface ...$middlewareDefinitions, ): void { if (!$this->isActive()) { return; diff --git a/src/Debug/QueueDecorator.php b/src/Debug/QueueDecorator.php index ed174393..20d3c7b8 100644 --- a/src/Debug/QueueDecorator.php +++ b/src/Debug/QueueDecorator.php @@ -7,7 +7,7 @@ use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; use Yiisoft\Queue\QueueInterface; final class QueueDecorator implements QueueInterface @@ -28,7 +28,7 @@ public function status(string|int $id): JobStatus public function push( MessageInterface $message, - string|array|callable|MiddlewarePushInterface ...$middlewareDefinitions + string|array|callable|MiddlewareInterface ...$middlewareDefinitions ): MessageInterface { $message = $this->queue->push($message, ...$middlewareDefinitions); $this->collector->collectPush($this->queue->getChannelName(), $message, ...$middlewareDefinitions); @@ -61,4 +61,9 @@ public function withChannelName(string $channel): QueueInterface $new->queue = $this->queue->withChannelName($channel); return $new; } + + public function getAdapter(): ?AdapterInterface + { + return $this->queue->getAdapter(); + } } diff --git a/src/Message/EnvelopeTrait.php b/src/Message/EnvelopeTrait.php index 7e58a97b..62fd2123 100644 --- a/src/Message/EnvelopeTrait.php +++ b/src/Message/EnvelopeTrait.php @@ -10,7 +10,11 @@ trait EnvelopeTrait public function getMessage(): MessageInterface { - return $this->message; + $message = $this->message; + while ($message instanceof EnvelopeInterface) { + $message = $message->getMessage(); + } + return $message; } public function withMessage(MessageInterface $message): self @@ -21,17 +25,12 @@ public function withMessage(MessageInterface $message): self return $instance; } - public function getHandlerName(): string - { - return $this->message->getHandlerName(); - } - public function getData(): mixed { return $this->message->getData(); } - public static function fromMessage(MessageInterface $message): self + public static function fromMessage(MessageInterface $message): EnvelopeInterface { return new static($message); } @@ -54,4 +53,20 @@ public function getEnvelopeMetadata(): array { return []; } + + public function withData(mixed $data): self + { + $instance = clone $this; + $instance->message = $instance->message->withData($data); + + return $instance; + } + + public function withMetadata(array $metadata): self + { + $instance = clone $this; + $instance->message = $instance->message->withMetadata($metadata); + + return $instance; + } } diff --git a/src/Middleware/FailureHandling/FailureEnvelope.php b/src/Message/FailureEnvelope.php similarity index 66% rename from src/Middleware/FailureHandling/FailureEnvelope.php rename to src/Message/FailureEnvelope.php index a8726285..b490ba80 100644 --- a/src/Middleware/FailureHandling/FailureEnvelope.php +++ b/src/Message/FailureEnvelope.php @@ -2,11 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\FailureHandling; - -use Yiisoft\Queue\Message\EnvelopeInterface; -use Yiisoft\Queue\Message\EnvelopeTrait; -use Yiisoft\Queue\Message\MessageInterface; +namespace Yiisoft\Queue\Message; final class FailureEnvelope implements EnvelopeInterface { diff --git a/src/Message/HandlerEnvelope.php b/src/Message/HandlerEnvelope.php new file mode 100644 index 00000000..578d418b --- /dev/null +++ b/src/Message/HandlerEnvelope.php @@ -0,0 +1,45 @@ + $handlerClass + */ + public function setHandler(string $handlerClass): void + { + $this->handlerClass = $handlerClass; + } + + /** + * @return class-string + */ + public function getHandler(): string + { + /** @psalm-suppress LessSpecificReturnStatement */ + return $this->handlerClass ?: $this->message->getMetadata()[self::HANDLER_CLASS_KEY]; + } + + public function getEnvelopeMetadata(): array + { + return [ + self::HANDLER_CLASS_KEY => $this->handlerClass, + ]; + } +} diff --git a/src/Message/IdEnvelope.php b/src/Message/IdEnvelope.php index dec2d679..32d5f3f8 100644 --- a/src/Message/IdEnvelope.php +++ b/src/Message/IdEnvelope.php @@ -29,7 +29,7 @@ public function getId(): string|int|null return $this->id ?? $this->message->getMetadata()[self::MESSAGE_ID_KEY] ?? null; } - private function getEnvelopeMetadata(): array + public function getEnvelopeMetadata(): array { return [self::MESSAGE_ID_KEY => $this->getId()]; } diff --git a/src/Message/JsonMessageSerializer.php b/src/Message/JsonMessageSerializer.php index 81a6220c..573d9a89 100644 --- a/src/Message/JsonMessageSerializer.php +++ b/src/Message/JsonMessageSerializer.php @@ -15,9 +15,9 @@ final class JsonMessageSerializer implements MessageSerializerInterface public function serialize(MessageInterface $message): string { $payload = [ - 'name' => $message->getHandlerName(), 'data' => $message->getData(), 'meta' => $message->getMetadata(), + 'class' => $message instanceof EnvelopeInterface ? $message->getMessage()::class : $message::class, ]; return json_encode($payload, JSON_THROW_ON_ERROR); @@ -38,13 +38,28 @@ public function unserialize(string $value): MessageInterface if (!is_array($meta)) { throw new InvalidArgumentException('Metadata must be array. Got ' . get_debug_type($meta) . '.'); } + $class = $payload['class'] ?? Message::class; - // TODO: will be removed later - $message = new Message($payload['name'] ?? '$name', $payload['data'] ?? null, $meta); + if (!is_subclass_of($class, MessageInterface::class)) { + throw new InvalidArgumentException(sprintf( + 'Class "%s" must implement "%s" interface.', + $class, + MessageInterface::class, + )); + } + + /** + * @var MessageInterface $message + */ + $message = new $class($payload['data'] ?? null, $meta); if (isset($meta[EnvelopeInterface::ENVELOPE_STACK_KEY]) && is_array($meta[EnvelopeInterface::ENVELOPE_STACK_KEY])) { $message = $message->withMetadata( - array_merge($message->getMetadata(), [EnvelopeInterface::ENVELOPE_STACK_KEY => []]), + array_merge( + $message->getMetadata(), + $meta, + [EnvelopeInterface::ENVELOPE_STACK_KEY => []], + ), ); foreach ($meta[EnvelopeInterface::ENVELOPE_STACK_KEY] as $envelope) { if (is_string($envelope) && class_exists($envelope) && is_subclass_of($envelope, EnvelopeInterface::class)) { @@ -53,7 +68,6 @@ public function unserialize(string $value): MessageInterface } } - return $message; } } diff --git a/src/Message/Message.php b/src/Message/Message.php index a414ffb0..4176ea3f 100644 --- a/src/Message/Message.php +++ b/src/Message/Message.php @@ -6,31 +6,18 @@ final class Message implements MessageInterface { + use MessageTrait; + /** * @param mixed $data Message data, encodable by a queue adapter * @param array $metadata Message metadata, encodable by a queue adapter - * @param string|null $id Message id */ public function __construct( - private string $handlerName, - private mixed $data, - private array $metadata = [], + mixed $data, + array $metadata = [], ) { - } - - public function getHandlerName(): string - { - return $this->handlerName; - } - - public function getData(): mixed - { - return $this->data; - } - - public function getMetadata(): array - { - return $this->metadata; + $this->data = $data; + $this->metadata = $metadata; } public function withMetadata(array $metadata): self diff --git a/src/Message/MessageHandlerInterface.php b/src/Message/MessageHandlerInterface.php new file mode 100644 index 00000000..7a8ab1f9 --- /dev/null +++ b/src/Message/MessageHandlerInterface.php @@ -0,0 +1,10 @@ +data; + } + + public function withData(mixed $data): self + { + $new = clone $this; + $new->data = $data; + return $new; + } + + public function getMetadata(): array + { + return $this->metadata; + } + + public function withMetadata(array $metadata): self + { + $new = clone $this; + $new->metadata = $metadata; + return $new; + } +} diff --git a/src/Middleware/Push/AdapterPushHandler.php b/src/Middleware/AdapterHandler.php similarity index 55% rename from src/Middleware/Push/AdapterPushHandler.php rename to src/Middleware/AdapterHandler.php index be4b1e67..7447280c 100644 --- a/src/Middleware/Push/AdapterPushHandler.php +++ b/src/Middleware/AdapterHandler.php @@ -2,18 +2,18 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\Push; +namespace Yiisoft\Queue\Middleware; use Yiisoft\Queue\Exception\AdapterConfiguration\AdapterNotConfiguredException; /** * @internal */ -final class AdapterPushHandler implements MessageHandlerPushInterface +final class AdapterHandler implements MessageHandlerInterface { - public function handlePush(PushRequest $request): PushRequest + public function handle(Request $request): Request { - if (($adapter = $request->getAdapter()) === null) { + if (($adapter = $request->getQueue()?->getAdapter()) === null) { throw new AdapterNotConfiguredException(); } return $request->withMessage($adapter->push($request->getMessage())); diff --git a/src/Middleware/Consume/ConsumeMiddlewareDispatcher.php b/src/Middleware/Consume/ConsumeMiddlewareDispatcher.php deleted file mode 100644 index be8de7df..00000000 --- a/src/Middleware/Consume/ConsumeMiddlewareDispatcher.php +++ /dev/null @@ -1,100 +0,0 @@ -middlewareDefinitions = array_reverse($middlewareDefinitions); - } - - /** - * Dispatch request through middleware to get response. - * - * @param ConsumeRequest $request Request to pass to middleware. - * @param MessageHandlerConsumeInterface $finishHandler Handler to use in case no middleware produced response. - */ - public function dispatch( - ConsumeRequest $request, - MessageHandlerConsumeInterface $finishHandler - ): ConsumeRequest { - if ($this->stack === null) { - $this->stack = new MiddlewareConsumeStack($this->buildMiddlewares(), $finishHandler); - } - - return $this->stack->handleConsume($request); - } - - /** - * Returns new instance with middleware handlers replaced with the ones provided. - * Last specified handler will be executed first. - * - * @param array[]|callable[]|MiddlewareConsumeInterface[]|string[] $middlewareDefinitions Each array element is: - * - * - A name of a middleware class. The middleware instance will be obtained from container executed. - * - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler): - * ResponseInterface` signature. - * - A "callable-like" array in format `[FooMiddleware::class, 'index']`. `FooMiddleware` instance will - * be created and `index()` method will be executed. - * - A function returning a middleware. The middleware returned will be executed. - * - * For callables typed parameters are automatically injected using dependency injection container. - * - * @return self New instance of the {@see ConsumeMiddlewareDispatcher} - */ - public function withMiddlewares(array $middlewareDefinitions): self - { - $instance = clone $this; - $instance->middlewareDefinitions = array_reverse($middlewareDefinitions); - - // Fixes a memory leak. - unset($instance->stack); - $instance->stack = null; - - return $instance; - } - - /** - * @return bool Whether there are middleware defined in the dispatcher. - */ - public function hasMiddlewares(): bool - { - return $this->middlewareDefinitions !== []; - } - - /** - * @return Closure[] - */ - private function buildMiddlewares(): array - { - $middlewares = []; - $factory = $this->middlewareFactory; - - foreach ($this->middlewareDefinitions as $middlewareDefinition) { - $middlewares[] = static fn (): MiddlewareConsumeInterface => $factory->createConsumeMiddleware( - $middlewareDefinition - ); - } - - return $middlewares; - } -} diff --git a/src/Middleware/Consume/MessageHandlerConsumeInterface.php b/src/Middleware/Consume/MessageHandlerConsumeInterface.php deleted file mode 100644 index 832d5896..00000000 --- a/src/Middleware/Consume/MessageHandlerConsumeInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -stack === null) { - $this->build(); - } - - /** @psalm-suppress PossiblyNullReference */ - return $this->stack->handleConsume($request); - } - - private function build(): void - { - $handler = $this->finishHandler; - - foreach ($this->middlewares as $middleware) { - $handler = $this->wrap($middleware, $handler); - } - - $this->stack = $handler; - } - - /** - * Wrap handler by middlewares. - */ - private function wrap(Closure $middlewareFactory, MessageHandlerConsumeInterface $handler): MessageHandlerConsumeInterface - { - return new class ($middlewareFactory, $handler) implements MessageHandlerConsumeInterface { - private ?MiddlewareConsumeInterface $middleware = null; - - public function __construct( - private Closure $middlewareFactory, - private MessageHandlerConsumeInterface $handler, - ) { - } - - public function handleConsume(ConsumeRequest $request): ConsumeRequest - { - if ($this->middleware === null) { - $this->middleware = ($this->middlewareFactory)(); - } - - return $this->middleware->processConsume($request, $this->handler); - } - }; - } -} diff --git a/src/Middleware/Consume/MiddlewareFactoryConsume.php b/src/Middleware/Consume/MiddlewareFactoryConsume.php deleted file mode 100644 index e5561b37..00000000 --- a/src/Middleware/Consume/MiddlewareFactoryConsume.php +++ /dev/null @@ -1,155 +0,0 @@ -getFromContainer($middlewareDefinition); - } - - return $this->tryGetFromCallable($middlewareDefinition) - ?? $this->tryGetFromArrayDefinition($middlewareDefinition) - ?? throw new InvalidMiddlewareDefinitionException($middlewareDefinition); - } - - private function getFromContainer(string $middlewareDefinition): MiddlewareConsumeInterface - { - if (class_exists($middlewareDefinition)) { - if (is_subclass_of($middlewareDefinition, MiddlewareConsumeInterface::class)) { - /** @var MiddlewareConsumeInterface */ - return $this->container->get($middlewareDefinition); - } - } elseif ($this->container->has($middlewareDefinition)) { - $middleware = $this->container->get($middlewareDefinition); - if ($middleware instanceof MiddlewareConsumeInterface) { - return $middleware; - } - } - - throw new InvalidMiddlewareDefinitionException($middlewareDefinition); - } - - private function wrapCallable(callable $callback): MiddlewareConsumeInterface - { - return new class ($callback, $this->container) implements MiddlewareConsumeInterface { - private $callback; - - public function __construct(callable $callback, private ContainerInterface $container) - { - $this->callback = $callback; - } - - public function processConsume(ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest - { - $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]); - if ($response instanceof ConsumeRequest) { - return $response; - } - - if ($response instanceof MiddlewareConsumeInterface) { - return $response->processConsume($request, $handler); - } - - throw new InvalidMiddlewareDefinitionException($this->callback); - } - }; - } - - private function tryGetFromCallable( - callable|MiddlewareConsumeInterface|array|string $definition - ): ?MiddlewareConsumeInterface { - if ($definition instanceof Closure) { - return $this->wrapCallable($definition); - } - - if ( - is_array($definition) - && array_keys($definition) === [0, 1] - ) { - try { - return $this->wrapCallable($this->callableFactory->create($definition)); - } catch (InvalidCallableConfigurationException $exception) { - throw new InvalidMiddlewareDefinitionException($definition, previous: $exception); - } - } else { - return null; - } - } - - private function tryGetFromArrayDefinition( - callable|MiddlewareConsumeInterface|array|string $definition - ): ?MiddlewareConsumeInterface { - if (!is_array($definition)) { - return null; - } - - try { - DefinitionValidator::validateArrayDefinition($definition); - - $middleware = ArrayDefinition::fromConfig($definition)->resolve($this->container); - if ($middleware instanceof MiddlewareConsumeInterface) { - return $middleware; - } - - throw new InvalidMiddlewareDefinitionException($definition); - } catch (InvalidConfigException) { - } - - throw new InvalidMiddlewareDefinitionException($definition); - } -} diff --git a/src/Middleware/Consume/MiddlewareFactoryConsumeInterface.php b/src/Middleware/Consume/MiddlewareFactoryConsumeInterface.php deleted file mode 100644 index 70f20cf2..00000000 --- a/src/Middleware/Consume/MiddlewareFactoryConsumeInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -handler; $handler($request->getMessage()); diff --git a/src/Middleware/Push/Implementation/DelayMiddlewareInterface.php b/src/Middleware/DelayMiddlewareInterface.php similarity index 69% rename from src/Middleware/Push/Implementation/DelayMiddlewareInterface.php rename to src/Middleware/DelayMiddlewareInterface.php index bd52a52e..6f99d4cc 100644 --- a/src/Middleware/Push/Implementation/DelayMiddlewareInterface.php +++ b/src/Middleware/DelayMiddlewareInterface.php @@ -2,14 +2,12 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\Push\Implementation; - -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; +namespace Yiisoft\Queue\Middleware; /** * A middleware interface for message delaying. It must be implemented in an adapter package or in a project. */ -interface DelayMiddlewareInterface extends MiddlewarePushInterface +interface DelayMiddlewareInterface extends MiddlewareInterface { /** * Set a new delay value into the middleware object diff --git a/src/Middleware/FailureHandling/Implementation/ExponentialDelayMiddleware.php b/src/Middleware/ExponentialDelayMiddleware.php similarity index 79% rename from src/Middleware/FailureHandling/Implementation/ExponentialDelayMiddleware.php rename to src/Middleware/ExponentialDelayMiddleware.php index 07d0f640..c464296a 100644 --- a/src/Middleware/FailureHandling/Implementation/ExponentialDelayMiddleware.php +++ b/src/Middleware/ExponentialDelayMiddleware.php @@ -2,23 +2,18 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\FailureHandling\Implementation; +namespace Yiisoft\Queue\Middleware; use InvalidArgumentException; -use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFailureInterface; -use Yiisoft\Queue\Middleware\Push\Implementation\DelayMiddlewareInterface; use Yiisoft\Queue\QueueInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureEnvelope; +use Yiisoft\Queue\Message\FailureEnvelope; /** * Failure strategy which resends the given message to a queue with an exponentially increasing delay. * The delay mechanism **must** be implemented by the used {@see AdapterInterface} implementation. */ -final class ExponentialDelayMiddleware implements MiddlewareFailureInterface +final class ExponentialDelayMiddleware implements MiddlewareInterface { public const META_KEY_ATTEMPTS = 'failure-strategy-exponential-delay-attempts'; public const META_KEY_DELAY = 'failure-strategy-exponential-delay-delay'; @@ -29,7 +24,6 @@ final class ExponentialDelayMiddleware implements MiddlewareFailureInterface * @param float $delayInitial The first delay period * @param float $delayMaximum The maximum delay period * @param float $exponent Message handling delay will be increased by this multiplication each time it fails - * @param QueueInterface|null $queue */ public function __construct( private string $id, @@ -38,7 +32,7 @@ public function __construct( private float $delayMaximum, private float $exponent, private DelayMiddlewareInterface $delayMiddleware, - private ?QueueInterface $queue = null, + private QueueInterface $queue, ) { if ($maxAttempts <= 0) { throw new InvalidArgumentException("maxAttempts parameter must be a positive integer, $this->maxAttempts given."); @@ -57,14 +51,14 @@ public function __construct( } } - public function processFailure( - FailureHandlingRequest $request, - MessageFailureHandlerInterface $handler - ): FailureHandlingRequest { + public function process( + Request $request, + MessageHandlerInterface $handler + ): Request { $message = $request->getMessage(); if ($this->suites($message)) { $envelope = new FailureEnvelope($message, $this->createNewMeta($message)); - $queue = $this->queue ?? $request->getQueue(); + $queue = $this->queue; $middlewareDefinitions = $this->delayMiddleware->withDelay($this->getDelay($envelope)); $messageNew = $queue->push( $envelope, @@ -74,7 +68,7 @@ public function processFailure( return $request->withMessage($messageNew); } - return $handler->handleFailure($request); + return $handler->handle($request); } private function suites(MessageInterface $message): bool diff --git a/src/Middleware/FailureFinalHandler.php b/src/Middleware/FailureFinalHandler.php new file mode 100644 index 00000000..dae7dfbb --- /dev/null +++ b/src/Middleware/FailureFinalHandler.php @@ -0,0 +1,27 @@ +getException() ?? new \RuntimeException('Message processing failed.'); + throw $this->exception; + } +} diff --git a/src/Middleware/FailureHandling/FailureFinalHandler.php b/src/Middleware/FailureHandling/FailureFinalHandler.php deleted file mode 100644 index f9668231..00000000 --- a/src/Middleware/FailureHandling/FailureFinalHandler.php +++ /dev/null @@ -1,21 +0,0 @@ -getException(); - } -} diff --git a/src/Middleware/FailureHandling/FailureHandlingRequest.php b/src/Middleware/FailureHandling/FailureHandlingRequest.php index 43de7433..3a5d8b92 100644 --- a/src/Middleware/FailureHandling/FailureHandlingRequest.php +++ b/src/Middleware/FailureHandling/FailureHandlingRequest.php @@ -6,53 +6,18 @@ use Throwable; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\QueueInterface; +use Yiisoft\Queue\Middleware\Request; -final class FailureHandlingRequest +// TODO: remove class +final class FailureHandlingRequest extends Request { - public function __construct(private MessageInterface $message, private Throwable $exception, private QueueInterface $queue) + public function __construct(private MessageInterface $message, private ?Throwable $exception) { + parent::__construct($message, null); } - /** - * @return MessageInterface - */ - public function getMessage(): MessageInterface - { - return $this->message; - } - - public function getException(): Throwable + public function getException(): ?Throwable { return $this->exception; } - - public function getQueue(): QueueInterface - { - return $this->queue; - } - - public function withMessage(MessageInterface $message): self - { - $instance = clone $this; - $instance->message = $message; - - return $instance; - } - - public function withException(Throwable $exception): self - { - $instance = clone $this; - $instance->exception = $exception; - - return $instance; - } - - public function withQueue(QueueInterface $queue): self - { - $instance = clone $this; - $instance->queue = $queue; - - return $instance; - } } diff --git a/src/Middleware/FailureHandling/FailureMiddlewareDispatcher.php b/src/Middleware/FailureHandling/FailureMiddlewareDispatcher.php deleted file mode 100644 index af11386e..00000000 --- a/src/Middleware/FailureHandling/FailureMiddlewareDispatcher.php +++ /dev/null @@ -1,106 +0,0 @@ -init(); - } - - /** - * Dispatch request through middleware to get response. - * - * @param FailureHandlingRequest $request Request to pass to middleware. - * @param MessageFailureHandlerInterface $finishHandler Handler to use in case no middleware produced response. - */ - public function dispatch( - FailureHandlingRequest $request, - MessageFailureHandlerInterface $finishHandler - ): FailureHandlingRequest { - $channelName = $request->getQueue()->getChannelName(); - if (!isset($this->middlewareDefinitions[$channelName]) || $this->middlewareDefinitions[$channelName] === []) { - $channelName = self::DEFAULT_PIPELINE; - } - $definitions = array_reverse($this->middlewareDefinitions[$channelName]); - - if (!isset($this->stack[$channelName])) { - $this->stack[$channelName] = new MiddlewareFailureStack($this->buildMiddlewares(...$definitions), $finishHandler); - } - - return $this->stack[$channelName]->handleFailure($request); - } - - /** - * Returns new instance with middleware handlers replaced with the ones provided. - * Last specified handler will be executed first. - * - * @param array[][]|callable[][]|MiddlewareFailureInterface[][]|string[][] $middlewareDefinitions Each array element is: - * - * - A name of a middleware class. The middleware instance will be obtained from container executed. - * - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler): - * ResponseInterface` signature. - * - A "callable-like" array in format `[FooMiddleware::class, 'index']`. `FooMiddleware` instance will - * be created and `index()` method will be executed. - * - A function returning a middleware. The middleware returned will be executed. - * - * For callables typed parameters are automatically injected using dependency injection container. - * - * @return self New instance of the {@see FailureMiddlewareDispatcher} - */ - public function withMiddlewares(array $middlewareDefinitions): self - { - $instance = clone $this; - $instance->middlewareDefinitions = $middlewareDefinitions; - - // Fixes a memory leak. - unset($instance->stack); - $instance->stack = []; - - $instance->init(); - - return $instance; - } - - private function init(): void - { - if (!isset($this->middlewareDefinitions[self::DEFAULT_PIPELINE])) { - $this->middlewareDefinitions[self::DEFAULT_PIPELINE] = []; - } - } - - /** - * @return Closure[] - */ - private function buildMiddlewares(array|callable|string|MiddlewareFailureInterface ...$definitions): array - { - $middlewares = []; - $factory = $this->middlewareFactory; - - foreach ($definitions as $middlewareDefinition) { - $middlewares[] = static fn (): MiddlewareFailureInterface => - $factory->createFailureMiddleware($middlewareDefinition); - } - - return $middlewares; - } -} diff --git a/src/Middleware/FailureHandling/MessageFailureHandlerInterface.php b/src/Middleware/FailureHandling/MessageFailureHandlerInterface.php deleted file mode 100644 index eada0b22..00000000 --- a/src/Middleware/FailureHandling/MessageFailureHandlerInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -getFromContainer($middlewareDefinition); - } - - return $this->tryGetFromCallable($middlewareDefinition) - ?? $this->tryGetFromArrayDefinition($middlewareDefinition) - ?? throw new InvalidMiddlewareDefinitionException($middlewareDefinition); - } - - private function getFromContainer(string $middlewareDefinition): MiddlewareFailureInterface - { - if (class_exists($middlewareDefinition)) { - if (is_subclass_of($middlewareDefinition, MiddlewareFailureInterface::class)) { - /** @var MiddlewareFailureInterface */ - return $this->container->get($middlewareDefinition); - } - } elseif ($this->container->has($middlewareDefinition)) { - $middleware = $this->container->get($middlewareDefinition); - if ($middleware instanceof MiddlewareFailureInterface) { - return $middleware; - } - } - - throw new InvalidMiddlewareDefinitionException($middlewareDefinition); - } - - private function wrapCallable(callable $callback): MiddlewareFailureInterface - { - return new class ($callback, $this->container) implements MiddlewareFailureInterface { - private $callback; - - public function __construct(callable $callback, private ContainerInterface $container) - { - $this->callback = $callback; - } - - public function processFailure(FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest - { - $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]); - if ($response instanceof FailureHandlingRequest) { - return $response; - } - - if ($response instanceof MiddlewareFailureInterface) { - return $response->processFailure($request, $handler); - } - - throw new InvalidMiddlewareDefinitionException($this->callback); - } - }; - } - - private function tryGetFromCallable( - callable|MiddlewareFailureInterface|array|string $definition - ): ?MiddlewareFailureInterface { - if ($definition instanceof Closure) { - return $this->wrapCallable($definition); - } - - if ( - is_array($definition) - && array_keys($definition) === [0, 1] - ) { - try { - return $this->wrapCallable($this->callableFactory->create($definition)); - } catch (InvalidCallableConfigurationException $exception) { - throw new InvalidMiddlewareDefinitionException($definition, previous: $exception); - } - } else { - return null; - } - } - - private function tryGetFromArrayDefinition( - callable|MiddlewareFailureInterface|array|string $definition - ): ?MiddlewareFailureInterface { - if (!is_array($definition)) { - return null; - } - - try { - DefinitionValidator::validateArrayDefinition($definition); - - $middleware = ArrayDefinition::fromConfig($definition)->resolve($this->container); - if ($middleware instanceof MiddlewareFailureInterface) { - return $middleware; - } - - throw new InvalidMiddlewareDefinitionException($definition); - } catch (InvalidConfigException) { - } - - throw new InvalidMiddlewareDefinitionException($definition); - } -} diff --git a/src/Middleware/FailureHandling/MiddlewareFactoryFailureInterface.php b/src/Middleware/FailureHandling/MiddlewareFactoryFailureInterface.php deleted file mode 100644 index 50f10aa5..00000000 --- a/src/Middleware/FailureHandling/MiddlewareFactoryFailureInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -stack === null) { - $this->build(); - } - - /** @psalm-suppress PossiblyNullReference */ - return $this->stack->handleFailure($request); - } - - private function build(): void - { - $handler = $this->finishHandler; - - foreach ($this->middlewares as $middleware) { - $handler = $this->wrap($middleware, $handler); - } - - $this->stack = $handler; - } - - /** - * Wrap handler by middlewares. - */ - private function wrap(Closure $middlewareFactory, MessageFailureHandlerInterface $handler): MessageFailureHandlerInterface - { - return new class ($middlewareFactory, $handler) implements MessageFailureHandlerInterface { - private ?MiddlewareFailureInterface $middleware = null; - - public function __construct( - private Closure $middlewareFactory, - private MessageFailureHandlerInterface $handler, - ) { - } - - public function handleFailure(FailureHandlingRequest $request): FailureHandlingRequest - { - if ($this->middleware === null) { - $this->middleware = ($this->middlewareFactory)(); - } - - return $this->middleware->processFailure($request, $this->handler); - } - }; - } -} diff --git a/src/Middleware/MessageHandlerInterface.php b/src/Middleware/MessageHandlerInterface.php new file mode 100644 index 00000000..1191b63f --- /dev/null +++ b/src/Middleware/MessageHandlerInterface.php @@ -0,0 +1,10 @@ +middlewareDefinitions = array_reverse($middlewareDefinitions); } @@ -29,25 +29,25 @@ public function __construct( /** * Dispatch request through middleware to get response. * - * @param PushRequest $request Request to pass to middleware. - * @param MessageHandlerPushInterface $finishHandler Handler to use in case no middleware produced response. + * @param Request $request Request to pass to middleware. + * @param MessageHandlerInterface $finishHandler Handler to use in case no middleware produced response. */ public function dispatch( - PushRequest $request, - MessageHandlerPushInterface $finishHandler - ): PushRequest { + Request $request, + MessageHandlerInterface $finishHandler + ): Request { if ($this->stack === null) { - $this->stack = new MiddlewarePushStack($this->buildMiddlewares(), $finishHandler); + $this->stack = new MiddlewareStack($this->buildMiddlewares(), $finishHandler); } - return $this->stack->handlePush($request); + return $this->stack->handle($request); } /** * Returns new instance with middleware handlers replaced with the ones provided. * Last specified handler will be executed first. * - * @param array[]|callable[]|MiddlewarePushInterface[]|string[] $middlewareDefinitions Each array element is: + * @param array[]|callable[]|MiddlewareInterface[]|string[] $middlewareDefinitions Each array element is: * * - A name of a middleware class. The middleware instance will be obtained from container executed. * - A callable with `function(ServerRequestInterface $request, RequestHandlerInterface $handler): @@ -89,7 +89,7 @@ private function buildMiddlewares(): array $factory = $this->middlewareFactory; foreach ($this->middlewareDefinitions as $middlewareDefinition) { - $middlewares[] = static fn (): MiddlewarePushInterface => $factory->createPushMiddleware( + $middlewares[] = static fn (): MiddlewareInterface => $factory->createMiddleware( $middlewareDefinition ); } diff --git a/src/Middleware/Push/MiddlewareFactoryPush.php b/src/Middleware/MiddlewareFactory.php similarity index 73% rename from src/Middleware/Push/MiddlewareFactoryPush.php rename to src/Middleware/MiddlewareFactory.php index 8e235b36..87b9d22d 100644 --- a/src/Middleware/Push/MiddlewareFactoryPush.php +++ b/src/Middleware/MiddlewareFactory.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\Push; +namespace Yiisoft\Queue\Middleware; use Closure; use Psr\Container\ContainerInterface; @@ -10,16 +10,13 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Helpers\DefinitionValidator; use Yiisoft\Injector\Injector; -use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\InvalidCallableConfigurationException; -use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; use function is_string; /** * Creates a middleware based on the definition provided. */ -final class MiddlewareFactoryPush implements MiddlewareFactoryPushInterface +final class MiddlewareFactory implements MiddlewareFactoryInterface { /** * @param ContainerInterface $container Container to use for resolving definitions. @@ -31,7 +28,7 @@ public function __construct( } /** - * @param array|callable|MiddlewarePushInterface|string $middlewareDefinition Middleware definition in one of the + * @param array|callable|MiddlewareInterface|string $middlewareDefinition Middleware definition in one of the * following formats: * * - A middleware object. @@ -49,12 +46,12 @@ public function __construct( * * @throws InvalidMiddlewareDefinitionException * - * @return MiddlewarePushInterface + * @return MiddlewareInterface */ - public function createPushMiddleware( - MiddlewarePushInterface|callable|array|string $middlewareDefinition - ): MiddlewarePushInterface { - if ($middlewareDefinition instanceof MiddlewarePushInterface) { + public function createMiddleware( + MiddlewareInterface|callable|array|string $middlewareDefinition + ): MiddlewareInterface { + if ($middlewareDefinition instanceof MiddlewareInterface) { return $middlewareDefinition; } @@ -67,16 +64,16 @@ public function createPushMiddleware( ?? throw new InvalidMiddlewareDefinitionException($middlewareDefinition); } - private function getFromContainer(string $middlewareDefinition): MiddlewarePushInterface + private function getFromContainer(string $middlewareDefinition): MiddlewareInterface { if (class_exists($middlewareDefinition)) { - if (is_subclass_of($middlewareDefinition, MiddlewarePushInterface::class)) { - /** @var MiddlewarePushInterface */ + if (is_subclass_of($middlewareDefinition, MiddlewareInterface::class)) { + /** @var MiddlewareInterface */ return $this->container->get($middlewareDefinition); } } elseif ($this->container->has($middlewareDefinition)) { $middleware = $this->container->get($middlewareDefinition); - if ($middleware instanceof MiddlewarePushInterface) { + if ($middleware instanceof MiddlewareInterface) { return $middleware; } } @@ -84,9 +81,9 @@ private function getFromContainer(string $middlewareDefinition): MiddlewarePushI throw new InvalidMiddlewareDefinitionException($middlewareDefinition); } - private function wrapCallable(callable $callback): MiddlewarePushInterface + private function wrapCallable(callable $callback): MiddlewareInterface { - return new class ($callback, $this->container) implements MiddlewarePushInterface { + return new class ($callback, $this->container) implements MiddlewareInterface { private $callback; public function __construct(callable $callback, private ContainerInterface $container) @@ -94,15 +91,15 @@ public function __construct(callable $callback, private ContainerInterface $cont $this->callback = $callback; } - public function processPush(PushRequest $request, MessageHandlerPushInterface $handler): PushRequest + public function process(Request $request, MessageHandlerInterface $handler): Request { $response = (new Injector($this->container))->invoke($this->callback, [$request, $handler]); - if ($response instanceof PushRequest) { + if ($response instanceof Request) { return $response; } - if ($response instanceof MiddlewarePushInterface) { - return $response->processPush($request, $handler); + if ($response instanceof MiddlewareInterface) { + return $response->process($request, $handler); } throw new InvalidMiddlewareDefinitionException($this->callback); @@ -111,8 +108,8 @@ public function processPush(PushRequest $request, MessageHandlerPushInterface $h } private function tryGetFromCallable( - callable|MiddlewarePushInterface|array|string $definition - ): ?MiddlewarePushInterface { + callable|MiddlewareInterface|array|string $definition + ): ?MiddlewareInterface { if ($definition instanceof Closure) { return $this->wrapCallable($definition); } @@ -132,8 +129,8 @@ private function tryGetFromCallable( } private function tryGetFromArrayDefinition( - callable|MiddlewarePushInterface|array|string $definition - ): ?MiddlewarePushInterface { + callable|MiddlewareInterface|array|string $definition + ): ?MiddlewareInterface { if (!is_array($definition)) { return null; } @@ -142,7 +139,7 @@ private function tryGetFromArrayDefinition( DefinitionValidator::validateArrayDefinition($definition); $middleware = ArrayDefinition::fromConfig($definition)->resolve($this->container); - if ($middleware instanceof MiddlewarePushInterface) { + if ($middleware instanceof MiddlewareInterface) { return $middleware; } @@ -150,6 +147,6 @@ private function tryGetFromArrayDefinition( } catch (InvalidConfigException) { } - throw new InvalidMiddlewareDefinitionException($definition); + return null; } } diff --git a/src/Middleware/MiddlewareFactoryInterface.php b/src/Middleware/MiddlewareFactoryInterface.php new file mode 100644 index 00000000..6a445062 --- /dev/null +++ b/src/Middleware/MiddlewareFactoryInterface.php @@ -0,0 +1,22 @@ +stack === null) { $this->build(); } /** @psalm-suppress PossiblyNullReference */ - return $this->stack->handlePush($request); + return $this->stack->handle($request); } private function build(): void @@ -51,24 +51,24 @@ private function build(): void /** * Wrap handler by middlewares. */ - private function wrap(Closure $middlewareFactory, MessageHandlerPushInterface $handler): MessageHandlerPushInterface + private function wrap(Closure $middlewareFactory, MessageHandlerInterface $handler): MessageHandlerInterface { - return new class ($middlewareFactory, $handler) implements MessageHandlerPushInterface { - private ?MiddlewarePushInterface $middleware = null; + return new class ($middlewareFactory, $handler) implements MessageHandlerInterface { + private ?MiddlewareInterface $middleware = null; public function __construct( private Closure $middlewareFactory, - private MessageHandlerPushInterface $handler, + private MessageHandlerInterface $handler, ) { } - public function handlePush(PushRequest $request): PushRequest + public function handle(Request $request): Request { if ($this->middleware === null) { $this->middleware = ($this->middlewareFactory)(); } - return $this->middleware->processPush($request, $this->handler); + return $this->middleware->process($request, $this->handler); } }; } diff --git a/src/Middleware/Push/MessageHandlerPushInterface.php b/src/Middleware/Push/MessageHandlerPushInterface.php deleted file mode 100644 index 8db45bff..00000000 --- a/src/Middleware/Push/MessageHandlerPushInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -message; - } - - public function getAdapter(): ?AdapterInterface - { - return $this->adapter; - } - - public function withMessage(MessageInterface $message): self - { - $instance = clone $this; - $instance->message = $message; - - return $instance; - } - - public function withAdapter(AdapterInterface $adapter): self - { - $instance = clone $this; - $instance->adapter = $adapter; - - return $instance; - } -} diff --git a/src/Middleware/Consume/ConsumeRequest.php b/src/Middleware/Request.php similarity index 79% rename from src/Middleware/Consume/ConsumeRequest.php rename to src/Middleware/Request.php index 489c7d5a..fc4d09aa 100644 --- a/src/Middleware/Consume/ConsumeRequest.php +++ b/src/Middleware/Request.php @@ -2,14 +2,16 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\Consume; +namespace Yiisoft\Queue\Middleware; use Yiisoft\Queue\Message\MessageInterface; use Yiisoft\Queue\QueueInterface; -final class ConsumeRequest +// TODO: fix later +//final class Request +class Request { - public function __construct(private MessageInterface $message, private QueueInterface $queue) + public function __construct(private MessageInterface $message, private ?QueueInterface $queue) { } @@ -18,11 +20,6 @@ public function getMessage(): MessageInterface return $this->message; } - public function getQueue(): QueueInterface - { - return $this->queue; - } - public function withMessage(MessageInterface $message): self { $instance = clone $this; @@ -31,6 +28,11 @@ public function withMessage(MessageInterface $message): self return $instance; } + public function getQueue(): ?QueueInterface + { + return $this->queue; + } + public function withQueue(QueueInterface $queue): self { $instance = clone $this; diff --git a/src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php b/src/Middleware/SendAgainMiddleware.php similarity index 64% rename from src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php rename to src/Middleware/SendAgainMiddleware.php index 43ac16f4..bf212945 100644 --- a/src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php +++ b/src/Middleware/SendAgainMiddleware.php @@ -2,52 +2,46 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Middleware\FailureHandling\Implementation; +namespace Yiisoft\Queue\Middleware; use InvalidArgumentException; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureEnvelope; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFailureInterface; +use Yiisoft\Queue\Message\FailureEnvelope; use Yiisoft\Queue\QueueInterface; /** * Failure strategy which resends the given message to a queue. */ -final class SendAgainMiddleware implements MiddlewareFailureInterface +final class SendAgainMiddleware implements MiddlewareInterface { public const META_KEY_RESEND = 'failure-strategy-resend-attempts'; /** * @param string $id A unique id to differentiate two and more objects of this class * @param int $maxAttempts Maximum attempts count for this strategy with the given $id before it will give up - * @param QueueInterface|null $queue */ public function __construct( private string $id, private int $maxAttempts, - private ?QueueInterface $queue = null, + private QueueInterface $queue ) { if ($maxAttempts < 1) { throw new InvalidArgumentException("maxAttempts parameter must be a positive integer, $this->maxAttempts given."); } } - public function processFailure( - FailureHandlingRequest $request, - MessageFailureHandlerInterface $handler - ): FailureHandlingRequest { + public function process(Request $request, MessageHandlerInterface $handler): Request + { $message = $request->getMessage(); if ($this->suites($message)) { $envelope = new FailureEnvelope($message, $this->createMeta($message)); - $envelope = ($this->queue ?? $request->getQueue())->push($envelope); + $envelope = $this->queue->push($envelope); - return $request->withMessage($envelope) - ->withQueue($this->queue ?? $request->getQueue()); + $request = $request->withMessage($envelope); + return $request->withQueue($this->queue); } - return $handler->handleFailure($request); + return $handler->handle($request); } private function suites(MessageInterface $message): bool diff --git a/src/Queue.php b/src/Queue.php index ad8ea76b..5e6a75f3 100644 --- a/src/Queue.php +++ b/src/Queue.php @@ -9,34 +9,34 @@ use Yiisoft\Queue\Cli\LoopInterface; use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Exception\AdapterConfiguration\AdapterNotConfiguredException; +use Yiisoft\Queue\Message\IdEnvelope; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Push\AdapterPushHandler; -use Yiisoft\Queue\Middleware\Push\MessageHandlerPushInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Push\PushRequest; +use Yiisoft\Queue\Middleware\AdapterHandler; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\Worker\WorkerInterface; -use Yiisoft\Queue\Message\IdEnvelope; final class Queue implements QueueInterface { /** - * @var array|array[]|callable[]|MiddlewarePushInterface[]|string[] + * @var array|array[]|callable[]|MiddlewareInterface[]|string[] */ private array $middlewareDefinitions; - private AdapterPushHandler $adapterPushHandler; + private AdapterHandler $adapterHandler; public function __construct( private WorkerInterface $worker, private LoopInterface $loop, private LoggerInterface $logger, - private PushMiddlewareDispatcher $pushMiddlewareDispatcher, + private MiddlewareDispatcher $pushMiddlewareDispatcher, private ?AdapterInterface $adapter = null, private string $channelName = QueueFactoryInterface::DEFAULT_CHANNEL_NAME, - MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions + MiddlewareInterface|callable|array|string ...$middlewareDefinitions ) { $this->middlewareDefinitions = $middlewareDefinitions; - $this->adapterPushHandler = new AdapterPushHandler(); + $this->adapterHandler = new AdapterHandler(); } public function getChannelName(): string @@ -46,22 +46,22 @@ public function getChannelName(): string public function push( MessageInterface $message, - MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions + MiddlewareInterface|callable|array|string ...$middlewareDefinitions ): MessageInterface { $this->logger->debug( - 'Preparing to push message with handler name "{handlerName}".', - ['handlerName' => $message->getHandlerName()] + 'Preparing to push message with data "{data}" and metadata: "{metadata}.', + ['data' => json_encode($message->getData()), 'metadata' => json_encode($message->getMetadata())] ); - $request = new PushRequest($message, $this->adapter); + $request = new Request($message, $this); $message = $this->pushMiddlewareDispatcher - ->dispatch($request, $this->createPushHandler($middlewareDefinitions)) + ->dispatch($request, $this->createHandler($middlewareDefinitions)) ->getMessage(); $messageId = $message->getMetadata()[IdEnvelope::MESSAGE_ID_KEY] ?? 'null'; $this->logger->info( - 'Pushed message with handler name "{handlerName}" to the queue. Assigned ID #{id}.', - ['handlerName' => $message->getHandlerName(), 'id' => $messageId] + 'Pushed message id: "{id}".', + ['id' => $messageId] ); return $message; @@ -120,7 +120,12 @@ public function withAdapter(AdapterInterface $adapter): self return $new; } - public function withMiddlewares(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + public function getAdapter(): ?AdapterInterface + { + return $this->adapter; + } + + public function withMiddlewares(MiddlewareInterface|callable|array|string ...$middlewareDefinitions): self { $instance = clone $this; $instance->middlewareDefinitions = $middlewareDefinitions; @@ -128,10 +133,13 @@ public function withMiddlewares(MiddlewarePushInterface|callable|array|string .. return $instance; } - public function withMiddlewaresAdded(MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): self + public function withMiddlewaresAdded(MiddlewareInterface|callable|array|string ...$middlewareDefinitions): self { $instance = clone $this; - $instance->middlewareDefinitions = [...array_values($instance->middlewareDefinitions), ...array_values($middlewareDefinitions)]; + $instance->middlewareDefinitions = [ + ...array_values($instance->middlewareDefinitions), + ...array_values($middlewareDefinitions), + ]; return $instance; } @@ -158,25 +166,25 @@ private function checkAdapter(): void } } - private function createPushHandler(array $middlewares): MessageHandlerPushInterface + private function createHandler(array $middlewares): MessageHandlerInterface { return new class ( - $this->adapterPushHandler, + $this->adapterHandler, $this->pushMiddlewareDispatcher, array_merge($this->middlewareDefinitions, $middlewares) - ) implements MessageHandlerPushInterface { + ) implements MessageHandlerInterface { public function __construct( - private AdapterPushHandler $adapterPushHandler, - private PushMiddlewareDispatcher $dispatcher, + private AdapterHandler $adapterHandler, + private MiddlewareDispatcher $dispatcher, private array $middlewares, ) { } - public function handlePush(PushRequest $request): PushRequest + public function handle(Request $request): Request { return $this->dispatcher ->withMiddlewares($this->middlewares) - ->dispatch($request, $this->adapterPushHandler); + ->dispatch($request, $this->adapterHandler); } }; } diff --git a/src/QueueInterface.php b/src/QueueInterface.php index 489909e1..6f82e70d 100644 --- a/src/QueueInterface.php +++ b/src/QueueInterface.php @@ -8,7 +8,7 @@ use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; /** * @internal Please don't use this interface. It is only used here to make tests simpler and will be removed @@ -19,10 +19,10 @@ interface QueueInterface /** * Pushes a message into the queue. * - * @param array|callable|MiddlewarePushInterface|string ...$middlewareDefinitions + * @param array|callable|MiddlewareInterface|string ...$middlewareDefinitions * @return MessageInterface */ - public function push(MessageInterface $message, MiddlewarePushInterface|callable|array|string ...$middlewareDefinitions): MessageInterface; + public function push(MessageInterface $message, MiddlewareInterface|callable|array|string ...$middlewareDefinitions): MessageInterface; /** * Execute all existing jobs and exit @@ -47,6 +47,8 @@ public function status(string|int $id): JobStatus; public function withAdapter(AdapterInterface $adapter): self; + public function getAdapter(): ?AdapterInterface; + public function getChannelName(): string; public function withChannelName(string $channel): self; diff --git a/src/Worker/Worker.php b/src/Worker/Worker.php index 5f8ac7bb..8d166e29 100644 --- a/src/Worker/Worker.php +++ b/src/Worker/Worker.php @@ -4,40 +4,28 @@ namespace Yiisoft\Queue\Worker; -use Closure; -use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; -use Psr\Container\NotFoundExceptionInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\LoggerInterface; -use ReflectionException; -use ReflectionMethod; -use RuntimeException; use Throwable; -use Yiisoft\Injector\Injector; use Yiisoft\Queue\Exception\JobFailureException; +use Yiisoft\Queue\Message\EnvelopeInterface; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Consume\ConsumeFinalHandler; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\ConsumeRequest; -use Yiisoft\Queue\Middleware\Consume\MessageHandlerConsumeInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureFinalHandler; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; +use Yiisoft\Queue\Middleware\ConsumeFinalHandler; +use Yiisoft\Queue\Middleware\FailureFinalHandler; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\QueueInterface; -use Yiisoft\Queue\Message\IdEnvelope; +use Yiisoft\VarDumper\VarDumper; final class Worker implements WorkerInterface { - private array $handlersCached = []; - public function __construct( - private array $handlers, private LoggerInterface $logger, - private Injector $injector, + private EventDispatcherInterface $eventDispatcher, private ContainerInterface $container, - private ConsumeMiddlewareDispatcher $consumeMiddlewareDispatcher, - private FailureMiddlewareDispatcher $failureMiddlewareDispatcher, + private MiddlewareDispatcher $consumeMiddlewareDispatcher, + private MiddlewareDispatcher $failureMiddlewareDispatcher, ) { } @@ -46,108 +34,35 @@ public function __construct( */ public function process(MessageInterface $message, QueueInterface $queue): MessageInterface { - $this->logger->info('Processing message #{message}.', ['message' => $message->getMetadata()[IdEnvelope::MESSAGE_ID_KEY] ?? 'null']); + $this->logger->info( + 'Processing message #{message}.', + ['message' => VarDumper::create($message)->asJson()] + ); - $name = $message->getHandlerName(); - $handler = $this->getHandler($name); - if ($handler === null) { - throw new RuntimeException(sprintf('Queue handler with name "%s" does not exist', $name)); - } + $request = new Request($message, $queue); + + $closure = function (object $message): mixed { + if ($message instanceof EnvelopeInterface) { + $message = $message->getMessage(); + } + + return $this->eventDispatcher->dispatch($message); + }; - $request = new ConsumeRequest($message, $queue); - $closure = fn (MessageInterface $message): mixed => $this->injector->invoke($handler, [$message]); try { - return $this->consumeMiddlewareDispatcher->dispatch($request, $this->createConsumeHandler($closure))->getMessage(); + $result = $this->consumeMiddlewareDispatcher->dispatch($request, new ConsumeFinalHandler($closure)); + return $result->getMessage(); } catch (Throwable $exception) { - $request = new FailureHandlingRequest($request->getMessage(), $exception, $request->getQueue()); - try { - $result = $this->failureMiddlewareDispatcher->dispatch($request, $this->createFailureHandler()); - $this->logger->info($exception->getMessage()); + $result = $this->failureMiddlewareDispatcher->dispatch($request, new FailureFinalHandler($exception)); + $this->logger->info($exception); return $result->getMessage(); } catch (Throwable $exception) { $exception = new JobFailureException($message, $exception); - $this->logger->error($exception->getMessage()); + $this->logger->error($exception); throw $exception; } } } - - private function getHandler(string $name): ?callable - { - if (!array_key_exists($name, $this->handlersCached)) { - $this->handlersCached[$name] = $this->prepare($this->handlers[$name] ?? null); - } - - return $this->handlersCached[$name]; - } - - /** - * Checks if the handler is a DI container alias - * - * @param array|callable|object|string|null $definition - * - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - */ - private function prepare(callable|object|array|string|null $definition): callable|null - { - if (is_string($definition) && $this->container->has($definition)) { - return $this->container->get($definition); - } - - if ( - is_array($definition) - && array_keys($definition) === [0, 1] - && is_string($definition[0]) - && is_string($definition[1]) - ) { - [$className, $methodName] = $definition; - - if (!class_exists($className) && $this->container->has($className)) { - return [ - $this->container->get($className), - $methodName, - ]; - } - - if (!class_exists($className)) { - $this->logger->error("$className doesn't exist."); - - return null; - } - - try { - $reflection = new ReflectionMethod($className, $methodName); - } catch (ReflectionException $e) { - $this->logger->error($e->getMessage()); - - return null; - } - if ($reflection->isStatic()) { - return [$className, $methodName]; - } - if ($this->container->has($className)) { - return [ - $this->container->get($className), - $methodName, - ]; - } - - return null; - } - - return $definition; - } - - private function createConsumeHandler(Closure $handler): MessageHandlerConsumeInterface - { - return new ConsumeFinalHandler($handler); - } - - private function createFailureHandler(): MessageFailureHandlerInterface - { - return new FailureFinalHandler(); - } } diff --git a/tests/App/DummyQueue.php b/tests/App/DummyQueue.php index 42125909..d40f27ae 100644 --- a/tests/App/DummyQueue.php +++ b/tests/App/DummyQueue.php @@ -8,7 +8,7 @@ use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; use Yiisoft\Queue\QueueInterface; final class DummyQueue implements QueueInterface @@ -19,7 +19,7 @@ public function __construct(private string $channelName) public function push( MessageInterface $message, - string|array|callable|MiddlewarePushInterface ...$middlewareDefinitions + string|array|callable|MiddlewareInterface ...$middlewareDefinitions ): MessageInterface { return $message; } @@ -52,4 +52,9 @@ public function withChannelName(string $channel): QueueInterface { throw new Exception('`withChannelName()` method is not implemented yet.'); } + + public function getAdapter(): ?AdapterInterface + { + return null; + } } diff --git a/tests/App/FakeHandler.php b/tests/App/FakeHandler.php index b2d2e49c..53a19cf5 100644 --- a/tests/App/FakeHandler.php +++ b/tests/App/FakeHandler.php @@ -6,8 +6,9 @@ use Yiisoft\Queue\Message\MessageInterface; use RuntimeException; +use Yiisoft\Queue\Message\MessageHandlerInterface; -final class FakeHandler +final class FakeHandler implements MessageHandlerInterface { public static array $processedMessages = []; @@ -26,13 +27,13 @@ public function execute(MessageInterface $message): void self::$processedMessages[] = $message; } - public static function staticExecute(MessageInterface $message): void + public function executeWithException(MessageInterface $message): never { - self::$processedMessages[] = $message; + throw new RuntimeException('Test exception'); } - public function executeWithException(MessageInterface $message): never + public function handle(MessageInterface $message): void { - throw new RuntimeException('Test exception'); + self::$processedMessages[] = $message; } } diff --git a/tests/App/FakeQueue.php b/tests/App/FakeQueue.php new file mode 100644 index 00000000..9b575cb0 --- /dev/null +++ b/tests/App/FakeQueue.php @@ -0,0 +1,64 @@ +adapter = $adapter; + + return $new; + } + + public function getChannelName(): string + { + return $this->channelName; + } + + public function withChannelName(string $channel): self + { + throw new Exception('`withChannelName()` method is not implemented yet.'); + } + + public function getAdapter(): ?AdapterInterface + { + return $this->adapter; + } +} diff --git a/tests/Integration/MessageConsumingTest.php b/tests/Integration/MessageConsumingTest.php index 6c30ba92..574cee1d 100644 --- a/tests/Integration/MessageConsumingTest.php +++ b/tests/Integration/MessageConsumingTest.php @@ -4,41 +4,46 @@ namespace Yiisoft\Queue\Tests\Integration; -use Psr\Container\ContainerInterface; use Psr\Log\NullLogger; -use Yiisoft\Injector\Injector; +use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; +use Yiisoft\EventDispatcher\Provider\ListenerCollection; +use Yiisoft\EventDispatcher\Provider\Provider; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsumeInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; +use Yiisoft\Queue\Tests\Support\StackMessageHandler; use Yiisoft\Queue\Tests\TestCase; use Yiisoft\Queue\Worker\Worker; final class MessageConsumingTest extends TestCase { - private array $messagesProcessed; - public function testMessagesConsumed(): void { - $this->messagesProcessed = []; + $stackMessageHandler = new StackMessageHandler(); - $container = $this->createMock(ContainerInterface::class); + $collection = (new ListenerCollection()); + $collection = $collection->add(fn (Message $message) => $stackMessageHandler->handle($message)); $worker = new Worker( - ['test' => fn (MessageInterface $message): mixed => $this->messagesProcessed[] = $message->getData()], new NullLogger(), - new Injector($container), - $container, - new ConsumeMiddlewareDispatcher($this->createMock(MiddlewareFactoryConsumeInterface::class)), - new FailureMiddlewareDispatcher($this->createMock(MiddlewareFactoryFailureInterface::class), []) + new Dispatcher(new Provider($collection)), + $this->createContainer([StackMessageHandler::class => $stackMessageHandler]), + new MiddlewareDispatcher($this->createMock(MiddlewareFactoryInterface::class)), + new MiddlewareDispatcher($this->createMock(MiddlewareFactoryInterface::class), []) ); $messages = [1, 'foo', 'bar-baz']; foreach ($messages as $message) { - $worker->process(new Message('test', $message), $this->getQueue()); + $worker->process( + new Message($message), + $this->getQueue() + ); } - $this->assertEquals($messages, $this->messagesProcessed); + $data = array_map( + fn (MessageInterface $message) => $message->getData(), + $stackMessageHandler->processedMessages + ); + $this->assertEquals($messages, $data); } } diff --git a/tests/Integration/MiddlewareTest.php b/tests/Integration/MiddlewareTest.php index ed8f6ad3..d8af4f18 100644 --- a/tests/Integration/MiddlewareTest.php +++ b/tests/Integration/MiddlewareTest.php @@ -8,30 +8,30 @@ use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use Yiisoft\Injector\Injector; -use Yiisoft\Test\Support\Container\SimpleContainer; -use Yiisoft\Test\Support\Log\SimpleLogger; +use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; +use Yiisoft\EventDispatcher\Provider\ListenerCollection; +use Yiisoft\EventDispatcher\Provider\Provider; use Yiisoft\Queue\Adapter\SynchronousAdapter; use Yiisoft\Queue\Cli\LoopInterface; +use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Message\MessageInterface; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsume; -use Yiisoft\Queue\Middleware\FailureHandling\FailureFinalHandler; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\Implementation\ExponentialDelayMiddleware; -use Yiisoft\Queue\Middleware\FailureHandling\Implementation\SendAgainMiddleware; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailure; -use Yiisoft\Queue\Middleware\Push\Implementation\DelayMiddlewareInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPush; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; +use Yiisoft\Queue\Middleware\DelayMiddlewareInterface; +use Yiisoft\Queue\Middleware\ExponentialDelayMiddleware; +use Yiisoft\Queue\Middleware\FailureFinalHandler; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\Request; +use Yiisoft\Queue\Middleware\SendAgainMiddleware; use Yiisoft\Queue\Queue; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\Integration\Support\TestMiddleware; +use Yiisoft\Queue\Tests\Support\NullMessageHandler; use Yiisoft\Queue\Worker\Worker; use Yiisoft\Queue\Worker\WorkerInterface; +use Yiisoft\Test\Support\Container\SimpleContainer; +use Yiisoft\Test\Support\Log\SimpleLogger; final class MiddlewareTest extends TestCase { @@ -48,8 +48,8 @@ public function testFullStackPush(): void 'message 2', ]; - $pushMiddlewareDispatcher = new PushMiddlewareDispatcher( - new MiddlewareFactoryPush( + $pushMiddlewareDispatcher = new MiddlewareDispatcher( + new MiddlewareFactory( $this->createMock(ContainerInterface::class), new CallableFactory( $this->createMock(ContainerInterface::class) @@ -63,17 +63,14 @@ public function testFullStackPush(): void $this->createMock(LoopInterface::class), $this->createMock(LoggerInterface::class), $pushMiddlewareDispatcher, - new SynchronousAdapter( - $this->createMock(WorkerInterface::class), - $this->createMock(QueueInterface::class), - ), + new SynchronousAdapter(new JsonMessageSerializer()), ); $queue = $queue ->withMiddlewares(new TestMiddleware('Won\'t be executed')) ->withMiddlewares(new TestMiddleware('channel 1'), new TestMiddleware('channel 2')) ->withMiddlewaresAdded(new TestMiddleware('channel 3')); - $message = new Message('test', ['initial']); + $message = new Message(['initial']); $messagePushed = $queue->push( $message, new TestMiddleware('message 1'), @@ -90,11 +87,16 @@ public function testFullStackConsume(): void 'common 1', 'common 2', ]; - $container = new SimpleContainer(); + $handler = new NullMessageHandler(); + $container = new SimpleContainer([]); + $listeners = (new ListenerCollection())->add( + fn (Message $message) => $handler->handle($message), + Message::class + ); $callableFactory = new CallableFactory($container); - $consumeMiddlewareDispatcher = new ConsumeMiddlewareDispatcher( - new MiddlewareFactoryConsume( + $consumeMiddlewareDispatcher = new MiddlewareDispatcher( + new MiddlewareFactory( $this->createMock(ContainerInterface::class), new CallableFactory( $this->createMock(ContainerInterface::class) @@ -104,21 +106,20 @@ public function testFullStackConsume(): void new TestMiddleware('common 2'), ); - $failureMiddlewareDispatcher = new FailureMiddlewareDispatcher( - new MiddlewareFactoryFailure($container, $callableFactory), + $failureMiddlewareDispatcher = new MiddlewareDispatcher( + new MiddlewareFactory($container, $callableFactory), [], ); $worker = new Worker( - ['test' => static fn () => true], new SimpleLogger(), - new Injector($container), + new Dispatcher(new Provider($listeners)), $container, $consumeMiddlewareDispatcher, $failureMiddlewareDispatcher, ); - $message = new Message('test', ['initial']); + $message = new Message(['initial']); $messageConsumed = $worker->process($message, $this->createMock(QueueInterface::class)); self::assertEquals($stack, $messageConsumed->getData()); @@ -126,59 +127,61 @@ public function testFullStackConsume(): void public function testFullStackFailure(): void { - $exception = new InvalidArgumentException('test'); - $this->expectExceptionObject($exception); - - $message = new Message('simple', null, []); + $message = new Message(null, []); $queueCallback = static fn (MessageInterface $message): MessageInterface => $message; $queue = $this->createMock(QueueInterface::class); - $container = new SimpleContainer([SendAgainMiddleware::class => new SendAgainMiddleware('test-container', 1, $queue)]); + $container = new SimpleContainer( + [SendAgainMiddleware::class => new SendAgainMiddleware('test-container', 1, $queue)] + ); $callableFactory = new CallableFactory($container); $queue->expects(self::exactly(7))->method('push')->willReturnCallback($queueCallback); $queue->method('getChannelName')->willReturn('simple'); $middlewares = [ - 'simple' => [ - new SendAgainMiddleware('test', 1, $queue), - [ - 'class' => SendAgainMiddleware::class, - '__construct()' => ['test-factory', 1, $queue], - ], - [ - new SendAgainMiddleware('test-callable', 1, $queue), - 'processFailure', - ], - fn (): SendAgainMiddleware => new SendAgainMiddleware('test-callable-2', 1, $queue), - SendAgainMiddleware::class, - new ExponentialDelayMiddleware( - 'test', - 2, - 1, - 5, - 2, - $this->createMock(DelayMiddlewareInterface::class), - $queue, - ), + new SendAgainMiddleware('test', 1, $queue), + [ + 'class' => SendAgainMiddleware::class, + '__construct()' => ['test-factory', 1, $queue], ], + [ + new SendAgainMiddleware('test-callable', 1, $queue), + 'process', + ], + fn (): SendAgainMiddleware => new SendAgainMiddleware('test-callable-2', 1, $queue), + SendAgainMiddleware::class, + new ExponentialDelayMiddleware( + 'test', + 2, + 1, + 5, + 2, + $this->createMock(DelayMiddlewareInterface::class), + $queue, + ), ]; - $dispatcher = new FailureMiddlewareDispatcher( - new MiddlewareFactoryFailure($container, $callableFactory), - $middlewares, + $dispatcher = new MiddlewareDispatcher( + new MiddlewareFactory($container, $callableFactory), + ...$middlewares, ); $iteration = 0; - $request = new FailureHandlingRequest($message, $exception, $queue); - $finalHandler = new FailureFinalHandler(); + $exception = new InvalidArgumentException('test'); + $request = new Request($message, $queue->getAdapter()); + $finalHandler = new FailureFinalHandler($exception); + + $ex = null; try { do { $request = $dispatcher->dispatch($request, $finalHandler); $iteration++; } while (true); - } catch (InvalidArgumentException $thrown) { - self::assertEquals(7, $iteration); - - throw $thrown; + } catch (InvalidArgumentException $e) { + $ex = $e; } + + self::assertEquals(7, $iteration); + $this->assertNotNull($ex); + self::assertSame($exception, $ex); } } diff --git a/tests/Integration/QueueFactoryTest.php b/tests/Integration/QueueFactoryTest.php index 50076896..efe56ef2 100644 --- a/tests/Integration/QueueFactoryTest.php +++ b/tests/Integration/QueueFactoryTest.php @@ -10,9 +10,10 @@ use Yiisoft\Injector\Injector; use Yiisoft\Queue\Adapter\SynchronousAdapter; use Yiisoft\Queue\Cli\LoopInterface; +use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPushInterface; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; use Yiisoft\Queue\Queue; use Yiisoft\Queue\QueueFactory; use Yiisoft\Queue\QueueFactoryInterface; @@ -33,7 +34,7 @@ public function testQuickChange(): void new CallableFactory($container), new Injector($container), true, - new SynchronousAdapter($worker, $queue) + new SynchronousAdapter(new JsonMessageSerializer()) ); $adapter = $factory->get('test-channel'); @@ -62,7 +63,7 @@ public function testConfiguredChange(): void new CallableFactory($container), new Injector($container), true, - new SynchronousAdapter($worker, $queue) + new SynchronousAdapter(new JsonMessageSerializer()) ); $queue = $factory->get('test-channel'); @@ -76,7 +77,7 @@ private function getDefaultQueue(WorkerInterface $worker): Queue $worker, $this->createMock(LoopInterface::class), $this->createMock(LoggerInterface::class), - new PushMiddlewareDispatcher($this->createMock(MiddlewareFactoryPushInterface::class)), + new MiddlewareDispatcher($this->createMock(MiddlewareFactoryInterface::class)), ); } } diff --git a/tests/Integration/Support/ConsumeMiddleware.php b/tests/Integration/Support/ConsumeMiddleware.php new file mode 100644 index 00000000..61889714 --- /dev/null +++ b/tests/Integration/Support/ConsumeMiddleware.php @@ -0,0 +1,27 @@ +getMessage(); + $stack = $message->getData(); + $stack[] = $this->stage; + $messageNew = new Message($stack); + + return $handler->handle($request->withMessage($messageNew)); + } +} diff --git a/tests/Integration/Support/TestMiddleware.php b/tests/Integration/Support/TestMiddleware.php index 54ea46bb..01fbf44d 100644 --- a/tests/Integration/Support/TestMiddleware.php +++ b/tests/Integration/Support/TestMiddleware.php @@ -4,37 +4,33 @@ namespace Yiisoft\Queue\Tests\Integration\Support; -use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Middleware\Consume\ConsumeRequest; -use Yiisoft\Queue\Middleware\Consume\MessageHandlerConsumeInterface; -use Yiisoft\Queue\Middleware\Consume\MiddlewareConsumeInterface; -use Yiisoft\Queue\Middleware\Push\MessageHandlerPushInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; -use Yiisoft\Queue\Middleware\Push\PushRequest; - -final class TestMiddleware implements MiddlewarePushInterface, MiddlewareConsumeInterface +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\Request; + +final class TestMiddleware implements MiddlewareInterface { public function __construct(private string $stage) { } - public function processPush(PushRequest $request, MessageHandlerPushInterface $handler): PushRequest + public function process(Request $request, MessageHandlerInterface $handler): Request { $message = $request->getMessage(); $stack = $message->getData(); $stack[] = $this->stage; - $messageNew = new Message($message->getHandlerName(), $stack); + $messageNew = $message->withData($stack); - return $handler->handlePush($request->withMessage($messageNew)); + return $handler->handle($request->withMessage($messageNew)); } - public function processConsume(ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest + public function process2(Request $request, MessageHandlerInterface $handler): Request { $message = $request->getMessage(); $stack = $message->getData(); $stack[] = $this->stage; - $messageNew = new Message($message->getHandlerName(), $stack); + $messageNew = $message->withData($stack); - return $handler->handleConsume($request->withMessage($messageNew)); + return $handler->handle($request->withMessage($messageNew)); } } diff --git a/tests/Shared/ExceptionMessage.php b/tests/Shared/ExceptionMessage.php new file mode 100644 index 00000000..d49b92a1 --- /dev/null +++ b/tests/Shared/ExceptionMessage.php @@ -0,0 +1,18 @@ +data = $data; + } +} diff --git a/tests/Shared/ExceptionMessageHandler.php b/tests/Shared/ExceptionMessageHandler.php new file mode 100644 index 00000000..dedc0fe4 --- /dev/null +++ b/tests/Shared/ExceptionMessageHandler.php @@ -0,0 +1,17 @@ +processedMessages[] = $message; + } +} diff --git a/tests/Support/ExceptionMessage.php b/tests/Support/ExceptionMessage.php new file mode 100644 index 00000000..65f2743b --- /dev/null +++ b/tests/Support/ExceptionMessage.php @@ -0,0 +1,18 @@ +data = $data; + } +} diff --git a/tests/Support/ExceptionMessageHandler.php b/tests/Support/ExceptionMessageHandler.php new file mode 100644 index 00000000..5222a609 --- /dev/null +++ b/tests/Support/ExceptionMessageHandler.php @@ -0,0 +1,17 @@ +processedMessages[] = $message; + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 8f64bd8c..ee4b39f0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -7,24 +7,29 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase as BaseTestCase; use Psr\Container\ContainerInterface; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\NullLogger; -use RuntimeException; -use Yiisoft\Injector\Injector; -use Yiisoft\Test\Support\Container\SimpleContainer; +use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; +use Yiisoft\EventDispatcher\Provider\ListenerCollection; +use Yiisoft\EventDispatcher\Provider\Provider; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Adapter\SynchronousAdapter; use Yiisoft\Queue\Cli\LoopInterface; use Yiisoft\Queue\Cli\SimpleLoop; +use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsume; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailure; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPush; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactory; use Yiisoft\Queue\Queue; +use Yiisoft\Queue\Tests\Support\ExceptionMessage; +use Yiisoft\Queue\Tests\Support\ExceptionMessageHandler; +use Yiisoft\Queue\Tests\Support\NullMessage; +use Yiisoft\Queue\Tests\Support\NullMessageHandler; +use Yiisoft\Queue\Tests\Support\StackMessage; +use Yiisoft\Queue\Tests\Support\StackMessageHandler; use Yiisoft\Queue\Worker\Worker; use Yiisoft\Queue\Worker\WorkerInterface; +use Yiisoft\Test\Support\Container\SimpleContainer; /** * Base Test Case. @@ -36,6 +41,7 @@ abstract class TestCase extends BaseTestCase protected ?AdapterInterface $adapter = null; protected ?LoopInterface $loop = null; protected ?WorkerInterface $worker = null; + protected ?EventDispatcherInterface $eventDispatcher = null; protected array $eventHandlers = []; protected int $executionTimes; @@ -57,11 +63,7 @@ protected function setUp(): void */ protected function getQueue(): Queue { - if ($this->queue === null) { - $this->queue = $this->createQueue(); - } - - return $this->queue; + return $this->queue ??= $this->createQueue(); } /** @@ -69,38 +71,44 @@ protected function getQueue(): Queue */ protected function getAdapter(): AdapterInterface { - if ($this->adapter === null) { - $this->adapter = $this->createAdapter($this->needsRealAdapter()); - } - - return $this->adapter; + return $this->adapter ??= $this->createAdapter($this->needsRealAdapter()); } protected function getLoop(): LoopInterface { - if ($this->loop === null) { - $this->loop = $this->createLoop(); - } - - return $this->loop; + return $this->loop ??= $this->createLoop(); } protected function getWorker(): WorkerInterface { - if ($this->worker === null) { - $this->worker = $this->createWorker(); - } + return $this->worker ??= new Worker( + new NullLogger(), + $this->createEventDispatcher(), + $this->createContainer(), + $this->getMiddlewareDispatcher(), + $this->getMiddlewareDispatcher(), + ); + } - return $this->worker; + protected function createEventDispatcher(): EventDispatcherInterface + { + $container = $this->getContainer(); + $listeners = new ListenerCollection(); + $listeners = $listeners + ->add(fn (NullMessage $message) => $container->get(NullMessageHandler::class)->handle($message)) + ->add(fn (StackMessage $message) => $container->get(StackMessageHandler::class)->handle($message)) + ->add(fn (ExceptionMessage $message) => $container->get(ExceptionMessageHandler::class)->handle($message)); + + return $this->eventDispatcher ??= new Dispatcher( + new Provider( + $listeners + ) + ); } protected function getContainer(): ContainerInterface { - if ($this->container === null) { - $this->container = $this->createContainer(); - } - - return $this->container; + return $this->container ??= $this->createContainer(); } protected function createQueue(): Queue @@ -109,14 +117,14 @@ protected function createQueue(): Queue $this->getWorker(), $this->getLoop(), new NullLogger(), - $this->getPushMiddlewareDispatcher(), + $this->getMiddlewareDispatcher(), ); } - protected function createAdapter(bool $realAdapter = false): AdapterInterface + protected function createAdapter(bool $realAdapter): AdapterInterface { if ($realAdapter) { - return new SynchronousAdapter($this->getWorker(), $this->createQueue()); + return new SynchronousAdapter(new JsonMessageSerializer()); } return $this->createMock(AdapterInterface::class); @@ -127,26 +135,19 @@ protected function createLoop(): LoopInterface return new SimpleLoop(); } - protected function createWorker(): WorkerInterface + protected function createContainer(array $definitions = []): ContainerInterface { - return new Worker( - $this->getMessageHandlers(), - new NullLogger(), - new Injector($this->getContainer()), - $this->getContainer(), - $this->getConsumeMiddlewareDispatcher(), - $this->getFailureMiddlewareDispatcher(), - ); + return new SimpleContainer($this->getContainerDefinitions($definitions)); } - protected function createContainer(): ContainerInterface + protected function getContainerDefinitions(array $definitions): array { - return new SimpleContainer($this->getContainerDefinitions()); - } - - protected function getContainerDefinitions(): array - { - return []; + return [ + NullMessageHandler::class => new NullMessageHandler(), + StackMessageHandler::class => new StackMessageHandler(), + ExceptionMessageHandler::class => new ExceptionMessageHandler(), + ...$definitions, + ]; } protected function setEventHandlers(callable ...$handlers): void @@ -159,56 +160,18 @@ protected function getEventHandlers(): array return $this->eventHandlers; } - protected function getMessageHandlers(): array - { - return [ - 'simple' => fn () => $this->executionTimes++, - 'exceptional' => function (): never { - $this->executionTimes++; - - throw new RuntimeException('test'); - }, - 'retryable' => function (): never { - $this->executionTimes++; - - throw new RuntimeException('test'); - }, - ]; - } - protected function needsRealAdapter(): bool { return false; } - protected function getPushMiddlewareDispatcher(): PushMiddlewareDispatcher - { - return new PushMiddlewareDispatcher( - new MiddlewareFactoryPush( - $this->getContainer(), - new CallableFactory($this->getContainer()), - ), - ); - } - - protected function getConsumeMiddlewareDispatcher(): ConsumeMiddlewareDispatcher - { - return new ConsumeMiddlewareDispatcher( - new MiddlewareFactoryConsume( - $this->getContainer(), - new CallableFactory($this->getContainer()), - ), - ); - } - - protected function getFailureMiddlewareDispatcher(): FailureMiddlewareDispatcher + protected function getMiddlewareDispatcher(): MiddlewareDispatcher { - return new FailureMiddlewareDispatcher( - new MiddlewareFactoryFailure( + return new MiddlewareDispatcher( + new MiddlewareFactory( $this->getContainer(), new CallableFactory($this->getContainer()), ), - [], ); } } diff --git a/tests/Unit/EnvelopeTest.php b/tests/Unit/EnvelopeTest.php index af412125..63c214ac 100644 --- a/tests/Unit/EnvelopeTest.php +++ b/tests/Unit/EnvelopeTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Unit; +namespace Yiisoft\Queue\Tests\Unit; use PHPUnit\Framework\TestCase; use Yiisoft\Queue\Message\EnvelopeInterface; @@ -13,7 +13,7 @@ final class EnvelopeTest extends TestCase { public function testEnvelopeStack(): void { - $message = new Message('handler', 'test'); + $message = new Message('test'); $message = new IdEnvelope($message, 'test-id'); $this->assertEquals('test', $message->getMessage()->getData()); @@ -28,7 +28,7 @@ public function testEnvelopeStack(): void public function testEnvelopeDuplicates(): void { - $message = new Message('handler', 'test'); + $message = new Message('test'); $message = new IdEnvelope($message, 'test-id'); $message = new IdEnvelope($message, 'test-id'); $message = new IdEnvelope($message, 'test-id'); diff --git a/tests/Unit/Message/JsonMessageSerializerTest.php b/tests/Unit/Message/JsonMessageSerializerTest.php index 776a9835..cae6fdb6 100644 --- a/tests/Unit/Message/JsonMessageSerializerTest.php +++ b/tests/Unit/Message/JsonMessageSerializerTest.php @@ -106,21 +106,24 @@ public function testUnserializeEnvelopeStack(): void public function testSerialize(): void { - $message = new Message('handler', 'test'); + $message = new Message('test'); $serializer = $this->createSerializer(); $json = $serializer->serialize($message); $this->assertEquals( - '{"name":"handler","data":"test","meta":[]}', + sprintf( + '{"data":"test","meta":[],"class":"%s"}', + str_replace('\\', '\\\\', Message::class), + ), $json, ); } public function testSerializeEnvelopeStack(): void { - $message = new Message('handler', 'test'); + $message = new Message('test'); $message = new IdEnvelope($message, 'test-id'); $serializer = $this->createSerializer(); @@ -129,9 +132,10 @@ public function testSerializeEnvelopeStack(): void $this->assertEquals( sprintf( - '{"name":"handler","data":"test","meta":{"envelopes":["%s"],"%s":"test-id"}}', + '{"data":"test","meta":{"envelopes":["%s"],"%s":"test-id"},"class":"%s"}', str_replace('\\', '\\\\', IdEnvelope::class), IdEnvelope::MESSAGE_ID_KEY, + str_replace('\\', '\\\\', Message::class), ), $json, ); diff --git a/tests/Unit/Middleware/Consume/ConsumeRequestTest.php b/tests/Unit/Middleware/Consume/ConsumeRequestTest.php index 6493fccd..b7d4b8a1 100644 --- a/tests/Unit/Middleware/Consume/ConsumeRequestTest.php +++ b/tests/Unit/Middleware/Consume/ConsumeRequestTest.php @@ -5,7 +5,7 @@ namespace Yiisoft\Queue\Tests\Unit\Middleware\Consume; use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Middleware\Consume\ConsumeRequest; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\TestCase; @@ -13,9 +13,9 @@ final class ConsumeRequestTest extends TestCase { public function testImmutable(): void { - $message = new Message('test', 'test'); + $message = new Message('test'); $queue = $this->createMock(QueueInterface::class); - $consumeRequest = new ConsumeRequest($message, $queue); + $consumeRequest = new Request($message, $queue); $this->assertNotSame($consumeRequest, $consumeRequest->withMessage($message)); $this->assertNotSame($consumeRequest, $consumeRequest->withQueue($queue)); diff --git a/tests/Unit/Middleware/Consume/InvalidMiddlewareDefinitionExceptionTest.php b/tests/Unit/Middleware/Consume/InvalidMiddlewareDefinitionExceptionTest.php index 756c616d..d0cb45cb 100644 --- a/tests/Unit/Middleware/Consume/InvalidMiddlewareDefinitionExceptionTest.php +++ b/tests/Unit/Middleware/Consume/InvalidMiddlewareDefinitionExceptionTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; use stdClass; use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; final class InvalidMiddlewareDefinitionExceptionTest extends TestCase { @@ -21,15 +21,24 @@ public static function dataBase(): array ], [ new TestCallableMiddleware(), - 'an instance of "Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware"', + sprintf( + 'an instance of "%s"', + TestCallableMiddleware::class, + ), ], [ [TestCallableMiddleware::class, 'notExistsAction'], - '["Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware", "notExistsAction"]', + sprintf( + '["%s", "notExistsAction"]', + TestCallableMiddleware::class, + ), ], [ ['class' => TestCallableMiddleware::class, 'index'], - '["class" => "Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware", "index"]', + sprintf( + '["class" => "%s", "index"]', + TestCallableMiddleware::class, + ), ], ]; } diff --git a/tests/Unit/Middleware/Consume/MiddlewareDispatcherTest.php b/tests/Unit/Middleware/Consume/MiddlewareDispatcherTest.php index 15d1356c..d75f015d 100644 --- a/tests/Unit/Middleware/Consume/MiddlewareDispatcherTest.php +++ b/tests/Unit/Middleware/Consume/MiddlewareDispatcherTest.php @@ -7,30 +7,30 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestMiddleware; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\ConsumeRequest; -use Yiisoft\Queue\Middleware\Consume\MessageHandlerConsumeInterface; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsume; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\Request; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareFactory; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeAdapter; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestMiddleware; final class MiddlewareDispatcherTest extends TestCase { public function testCallableMiddlewareCalled(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); $queue = $this->createMock(QueueInterface::class); $dispatcher = $this->createDispatcher()->withMiddlewares( [ - static function (ConsumeRequest $request) use ($queue): ConsumeRequest { - return $request->withMessage(new Message('test', 'New closure test data'))->withQueue($queue); + static function (Request $request) use ($queue): Request { + return $request->withMessage(new Message('New closure test data'))->withQueue($queue); }, ] ); @@ -41,7 +41,7 @@ static function (ConsumeRequest $request) use ($queue): ConsumeRequest { public function testArrayMiddlewareCallableDefinition(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -54,7 +54,7 @@ public function testArrayMiddlewareCallableDefinition(): void public function testFactoryArrayDefinition(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); $container = $this->createContainer(); $definition = [ 'class' => TestMiddleware::class, @@ -67,35 +67,35 @@ public function testFactoryArrayDefinition(): void public function testMiddlewareFullStackCalled(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); - $middleware1 = static function (ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest { - $request = $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'new test data')); + $middleware1 = static function (Request $request, MessageHandlerInterface $handler): Request { + $request = $request->withMessage($request->getMessage()->withData('new test data')); - return $handler->handleConsume($request); + return $handler->handle($request); }; - $middleware2 = static function (ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest { - $request = $request->withMessage(new Message('new handler', $request->getMessage()->getData())); + $middleware2 = static function (Request $request, MessageHandlerInterface $handler): Request { + $request = $request->withMessage($request->getMessage()->withMetadata(['new' => 'metadata'])); - return $handler->handleConsume($request); + return $handler->handle($request); }; $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('new test data', $request->getMessage()->getData()); - $this->assertSame('new handler', $request->getMessage()->getHandlerName()); + $this->assertSame(['new' => 'metadata'], $request->getMessage()->getMetadata()); } public function testMiddlewareStackInterrupted(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); - $middleware1 = static function (ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest { - return $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'first')); + $middleware1 = static function (Request $request, MessageHandlerInterface $handler): Request { + return $request->withMessage($request->getMessage()->withData('first')); }; - $middleware2 = static function (ConsumeRequest $request, MessageHandlerConsumeInterface $handler): ConsumeRequest { - return $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'second')); + $middleware2 = static function (Request $request, MessageHandlerInterface $handler): Request { + return $request->withMessage($request->getMessage()->withData('second')); }; $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); @@ -129,7 +129,7 @@ public function testImmutability(): void public function testResetStackOnWithMiddlewares(): void { - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -148,10 +148,10 @@ public function testResetStackOnWithMiddlewares(): void self::assertSame('New middleware test data', $request->getMessage()->getData()); } - private function getRequestHandler(): MessageHandlerConsumeInterface + private function getRequestHandler(): MessageHandlerInterface { - return new class () implements MessageHandlerConsumeInterface { - public function handleConsume(ConsumeRequest $request): ConsumeRequest + return new class () implements MessageHandlerInterface { + public function handle(Request $request): Request { return $request; } @@ -160,12 +160,12 @@ public function handleConsume(ConsumeRequest $request): ConsumeRequest private function createDispatcher( ContainerInterface $container = null, - ): ConsumeMiddlewareDispatcher { + ): MiddlewareDispatcher { $container ??= $this->createContainer([AdapterInterface::class => new FakeAdapter()]); $callableFactory = new CallableFactory($container); - return new ConsumeMiddlewareDispatcher( - new MiddlewareFactoryConsume($container, $callableFactory), + return new MiddlewareDispatcher( + new MiddlewareFactory($container, $callableFactory), ); } @@ -174,10 +174,10 @@ private function createContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getConsumeRequest(): ConsumeRequest + private function getRequest(): Request { - return new ConsumeRequest( - new Message('handler', 'data'), + return new Request( + new Message('data'), $this->createMock(QueueInterface::class) ); } diff --git a/tests/Unit/Middleware/Consume/MiddlewareFactoryTest.php b/tests/Unit/Middleware/Consume/MiddlewareFactoryTest.php index d61919cb..59f91bdb 100644 --- a/tests/Unit/Middleware/Consume/MiddlewareFactoryTest.php +++ b/tests/Unit/Middleware/Consume/MiddlewareFactoryTest.php @@ -7,49 +7,49 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Yiisoft\Queue\QueueInterface; +use Yiisoft\Queue\Tests\Integration\Support\ConsumeMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Consume\ConsumeRequest; -use Yiisoft\Queue\Middleware\Consume\MessageHandlerConsumeInterface; -use Yiisoft\Queue\Middleware\Consume\MiddlewareConsumeInterface; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsume; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsumeInterface; +use Yiisoft\Queue\Middleware\Request; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; -use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeAdapter; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\InvalidController; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestCallableMiddleware; -use Yiisoft\Queue\Tests\Unit\Middleware\Consume\Support\TestMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\InvalidController; final class MiddlewareFactoryTest extends TestCase { public function testCreateFromClassString(): void { - $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware(TestMiddleware::class); - self::assertInstanceOf(TestMiddleware::class, $middleware); + $container = $this->getContainer([ConsumeMiddleware::class => new ConsumeMiddleware('stage1')]); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(ConsumeMiddleware::class); + self::assertInstanceOf(ConsumeMiddleware::class, $middleware); } public function testCreateFromAliasString(): void { - $container = $this->getContainer(['test' => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware('test'); - self::assertInstanceOf(TestMiddleware::class, $middleware); + $container = $this->getContainer(['test' => new ConsumeMiddleware('stage1')]); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware('test'); + self::assertInstanceOf(ConsumeMiddleware::class, $middleware); } public function testCreateFromArray(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [TestCallableMiddleware::class, 'index'] ); self::assertSame( 'New test data', - $middleware->processConsume( - $this->getConsumeRequest(), - $this->createMock(MessageHandlerConsumeInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData(), ); } @@ -57,17 +57,17 @@ public function testCreateFromArray(): void public function testCreateFromClosureResponse(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( - fn (): ConsumeRequest => new ConsumeRequest( - new Message('test', 'test data'), + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( + fn (): Request => new Request( + new Message('test data'), $this->createMock(QueueInterface::class), ) ); self::assertSame( 'test data', - $middleware->processConsume( - $this->getConsumeRequest(), - $this->createMock(MessageHandlerConsumeInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData() ); } @@ -75,27 +75,35 @@ public function testCreateFromClosureResponse(): void public function testCreateFromClosureMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( - static fn (): MiddlewareConsumeInterface => new TestMiddleware() + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( + static fn (): MiddlewareInterface => new ConsumeMiddleware('stage1') ); + + $handler = $this->createMock(MessageHandlerInterface::class); + $handler->expects($this->once())->method('handle')->willReturnCallback( + static fn (Request $request): Request => $request->withMessage( + new Message('New middleware test data') + ) + ); + self::assertSame( 'New middleware test data', - $middleware->processConsume( - $this->getConsumeRequest(), - $this->createMock(MessageHandlerConsumeInterface::class) + $middleware->process( + $this->getRequest(), + $handler )->getMessage()->getData() ); } public function testCreateWithUseParamsMiddleware(): void { - $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware(TestMiddleware::class); + $container = $this->getContainer([ConsumeMiddleware::class => new ConsumeMiddleware('stage1')]); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(ConsumeMiddleware::class); self::assertSame( - 'New middleware test data', - $middleware->processConsume( - $this->getConsumeRequest(), + ['data', 'stage1'], + $middleware->process( + $this->getRequest(), $this->getRequestHandler() )->getMessage()->getData() ); @@ -104,14 +112,14 @@ public function testCreateWithUseParamsMiddleware(): void public function testCreateWithTestCallableMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [TestCallableMiddleware::class, 'index'] ); - $request = $this->getConsumeRequest(); + $request = $this->getRequest(); self::assertSame( 'New test data', - $middleware->processConsume( + $middleware->process( $request, $this->getRequestHandler() )->getMessage()->getData() @@ -121,14 +129,14 @@ public function testCreateWithTestCallableMiddleware(): void public function testInvalidMiddlewareWithWrongCallable(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( static fn () => 42 ); $this->expectException(InvalidMiddlewareDefinitionException::class); - $middleware->processConsume( - $this->getConsumeRequest(), - $this->createMock(MessageHandlerConsumeInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) ); } @@ -150,28 +158,28 @@ public static function invalidMiddlewareDefinitionProvider(): array public function testInvalidMiddleware(mixed $definition): void { $this->expectException(InvalidMiddlewareDefinitionException::class); - $this->getMiddlewareFactory()->createConsumeMiddleware($definition); + $this->getMiddlewareFactory()->createMiddleware($definition); } public function testInvalidMiddlewareWithWrongController(): void { $container = $this->getContainer([InvalidController::class => new InvalidController()]); - $middleware = $this->getMiddlewareFactory($container)->createConsumeMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [InvalidController::class, 'index'] ); $this->expectException(InvalidMiddlewareDefinitionException::class); - $middleware->processConsume( - $this->getConsumeRequest(), - $this->createMock(MessageHandlerConsumeInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) ); } - private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryConsumeInterface + private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryInterface { $container ??= $this->getContainer([AdapterInterface::class => new FakeAdapter()]); - return new MiddlewareFactoryConsume($container, new CallableFactory($container)); + return new MiddlewareFactory($container, new CallableFactory($container)); } private function getContainer(array $instances = []): ContainerInterface @@ -179,20 +187,20 @@ private function getContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getRequestHandler(): MessageHandlerConsumeInterface + private function getRequestHandler(): MessageHandlerInterface { - return new class () implements MessageHandlerConsumeInterface { - public function handleConsume(ConsumeRequest $request): ConsumeRequest + return new class () implements MessageHandlerInterface { + public function handle(Request $request): Request { return $request; } }; } - private function getConsumeRequest(): ConsumeRequest + private function getRequest(): Request { - return new ConsumeRequest( - new Message('handler', 'data'), + return new Request( + new Message(['data']), $this->createMock(QueueInterface::class) ); } diff --git a/tests/Unit/Middleware/Consume/Support/FailMiddleware.php b/tests/Unit/Middleware/Consume/Support/FailMiddleware.php deleted file mode 100644 index dd8ed5b7..00000000 --- a/tests/Unit/Middleware/Consume/Support/FailMiddleware.php +++ /dev/null @@ -1,18 +0,0 @@ -withMessage(new Message('test', 'New test data')); - } -} diff --git a/tests/Unit/Middleware/Consume/Support/TestMiddleware.php b/tests/Unit/Middleware/Consume/Support/TestMiddleware.php deleted file mode 100644 index 1e4508f0..00000000 --- a/tests/Unit/Middleware/Consume/Support/TestMiddleware.php +++ /dev/null @@ -1,22 +0,0 @@ -withMessage(new Message('test', $this->message)); - } -} diff --git a/tests/Unit/Middleware/FailureHandling/FailureHandlingRequestTest.php b/tests/Unit/Middleware/FailureHandling/FailureHandlingRequestTest.php deleted file mode 100644 index 5ee524ac..00000000 --- a/tests/Unit/Middleware/FailureHandling/FailureHandlingRequestTest.php +++ /dev/null @@ -1,26 +0,0 @@ -createMock(QueueInterface::class); - $failureHandlingRequest = new FailureHandlingRequest( - new Message('test', 'test'), - new Exception(), - $queue - ); - - $this->assertNotSame($failureHandlingRequest, $failureHandlingRequest->withQueue($queue)); - } -} diff --git a/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php b/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php index 5d24f46a..a81a7fb1 100644 --- a/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php +++ b/tests/Unit/Middleware/FailureHandling/Implementation/ExponentialDelayMiddlewareTest.php @@ -2,16 +2,16 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Implementation; +namespace Yiisoft\Queue\Tests\Unit\Middleware; use Exception; use InvalidArgumentException; use PHPUnit\Framework\Attributes\DataProvider; use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\Implementation\ExponentialDelayMiddleware; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\Push\Implementation\DelayMiddlewareInterface; +use Yiisoft\Queue\Middleware\DelayMiddlewareInterface; +use Yiisoft\Queue\Middleware\ExponentialDelayMiddleware; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\TestCase; @@ -129,7 +129,7 @@ public function testConstructorRequirements(bool $success, array $arguments): vo public function testPipelineSuccess(): void { - $message = new Message('test', null); + $message = new Message(null); $queue = $this->createMock(QueueInterface::class); $queue->method('push')->willReturnArgument(0); $middleware = new ExponentialDelayMiddleware( @@ -141,12 +141,12 @@ public function testPipelineSuccess(): void $this->createMock(DelayMiddlewareInterface::class), $queue, ); - $nextHandler = $this->createMock(MessageFailureHandlerInterface::class); - $nextHandler->expects(self::never())->method('handleFailure'); - $request = new FailureHandlingRequest($message, new Exception('test'), $queue); - $result = $middleware->processFailure($request, $nextHandler); + $nextHandler = $this->createMock(MessageHandlerInterface::class); + $nextHandler->expects(self::never())->method('handle'); + $request = new Request($message, null); + $result = $middleware->process($request, $nextHandler); - self::assertNotEquals($request, $result); + self::assertNotSame($request, $result); $message = $result->getMessage(); self::assertArrayHasKey(ExponentialDelayMiddleware::META_KEY_ATTEMPTS . '-test', $message->getMetadata()); self::assertArrayHasKey(ExponentialDelayMiddleware::META_KEY_DELAY . '-test', $message->getMetadata()); @@ -154,10 +154,7 @@ public function testPipelineSuccess(): void public function testPipelineFailure(): void { - $this->expectException(Exception::class); - $this->expectExceptionMessage('test'); - - $message = new Message('test', null, [ExponentialDelayMiddleware::META_KEY_ATTEMPTS . '-test' => 2]); + $message = new Message(null, [ExponentialDelayMiddleware::META_KEY_ATTEMPTS . '-test' => 2]); $queue = $this->createMock(QueueInterface::class); $middleware = new ExponentialDelayMiddleware( 'test', @@ -168,10 +165,12 @@ public function testPipelineFailure(): void $this->createMock(DelayMiddlewareInterface::class), $queue, ); - $nextHandler = $this->createMock(MessageFailureHandlerInterface::class); + $nextHandler = $this->createMock(MessageHandlerInterface::class); $exception = new Exception('test'); - $nextHandler->expects(self::once())->method('handleFailure')->willThrowException($exception); - $request = new FailureHandlingRequest($message, $exception, $queue); - $middleware->processFailure($request, $nextHandler); + $nextHandler->expects(self::once())->method('handle')->willThrowException($exception); + $request = new Request($message, null); + + $this->expectExceptionObject($exception); + $middleware->process($request, $nextHandler); } } diff --git a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php index 57dc4eb9..7d1eca56 100644 --- a/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php +++ b/tests/Unit/Middleware/FailureHandling/Implementation/SendAgainMiddlewareTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Implementation; +namespace Yiisoft\Queue\Tests\Unit\Middleware; use Exception; use PHPUnit\Framework\Assert; @@ -11,11 +11,11 @@ use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Message\MessageInterface; use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\Implementation\ExponentialDelayMiddleware; -use Yiisoft\Queue\Middleware\FailureHandling\Implementation\SendAgainMiddleware; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFailureInterface; -use Yiisoft\Queue\Middleware\Push\Implementation\DelayMiddlewareInterface; +use Yiisoft\Queue\Middleware\ExponentialDelayMiddleware; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\SendAgainMiddleware; +use Yiisoft\Queue\Middleware\DelayMiddlewareInterface; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\TestCase; @@ -156,19 +156,17 @@ public function testQueueSendingStrategies( $strategy = $this->getStrategy($strategyName, $queue); $request = new FailureHandlingRequest( new Message( - 'test', null, $metaInitial ), new Exception('testException'), - $queue ); - $result = $strategy->processFailure($request, $handler); + $result = $strategy->process($request, $handler); self::assertInstanceOf(FailureHandlingRequest::class, $result); } - private function getStrategy(string $strategyName, QueueInterface $queue): MiddlewareFailureInterface + private function getStrategy(string $strategyName, QueueInterface $queue): MiddlewareInterface { return match ($strategyName) { SendAgainMiddleware::class => new SendAgainMiddleware('', 2, $queue), @@ -181,22 +179,20 @@ private function getStrategy(string $strategyName, QueueInterface $queue): Middl $this->createMock(DelayMiddlewareInterface::class), $queue, ), - default => throw new RuntimeException('Unknown strategy'), + default => throw new RuntimeException(sprintf('Unknown strategy "%s"', $strategyName)), }; } - private function getHandler(array $metaResult, bool $suites): MessageFailureHandlerInterface + private function getHandler(array $metaResult, bool $suites): MessageHandlerInterface { - $pipelineAssertion = static function (FailureHandlingRequest $request) use ( - $metaResult - ): FailureHandlingRequest { + $pipelineAssertion = static function (FailureHandlingRequest $request) use ($metaResult): void { Assert::assertEquals($metaResult, $request->getMessage()->getMetadata()); throw $request->getException(); }; - $handler = $this->createMock(MessageFailureHandlerInterface::class); + $handler = $this->createMock(MessageHandlerInterface::class); $handler->expects($suites ? self::never() : self::once()) - ->method('handleFailure') + ->method('handle') ->willReturnCallback($pipelineAssertion); return $handler; diff --git a/tests/Unit/Middleware/FailureHandling/MiddlewareDispatcherTest.php b/tests/Unit/Middleware/FailureHandling/MiddlewareDispatcherTest.php index fd566690..e965c5f7 100644 --- a/tests/Unit/Middleware/FailureHandling/MiddlewareDispatcherTest.php +++ b/tests/Unit/Middleware/FailureHandling/MiddlewareDispatcherTest.php @@ -4,17 +4,16 @@ namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling; -use Exception; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailure; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeAdapter; use Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Support\TestCallableMiddleware; @@ -24,17 +23,13 @@ final class MiddlewareDispatcherTest extends TestCase { public function testCallableMiddlewareCalled(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); - $dispatcher = $this->createDispatcher()->withMiddlewares( - [ - FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [ - static function (FailureHandlingRequest $request): FailureHandlingRequest { - return $request->withMessage(new Message('test', 'New closure test data')); - }, - ], - ] - ); + $dispatcher = $this->createDispatcher()->withMiddlewares([ + static function (Request $request): Request { + return $request->withMessage(new Message('New closure test data')); + }, + ]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('New closure test data', $request->getMessage()->getData()); @@ -42,7 +37,7 @@ static function (FailureHandlingRequest $request): FailureHandlingRequest { public function testArrayMiddlewareCallableDefinition(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -50,62 +45,60 @@ public function testArrayMiddlewareCallableDefinition(): void ); $dispatcher = $this ->createDispatcher($container) - ->withMiddlewares( - [ - FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [[TestCallableMiddleware::class, 'index']], - ] - ); + ->withMiddlewares([ + [TestCallableMiddleware::class, 'index'], + ]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('New test data', $request->getMessage()->getData()); } public function testFactoryArrayDefinition(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); $container = $this->createContainer(); $definition = [ 'class' => TestMiddleware::class, '__construct()' => ['message' => 'New test data from the definition'], ]; - $dispatcher = $this->createDispatcher($container)->withMiddlewares([FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [$definition]]); + $dispatcher = $this->createDispatcher($container)->withMiddlewares([$definition]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('New test data from the definition', $request->getMessage()->getData()); } public function testMiddlewareFullStackCalled(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); - $middleware1 = static function (FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest { - $request = $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'new test data')); + $middleware1 = static function (Request $request, MessageHandlerInterface $handler): Request { + $request = $request->withMessage($request->getMessage()->withData('new test data')); - return $handler->handleFailure($request); + return $handler->handle($request); }; - $middleware2 = static function (FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest { - $request = $request->withMessage(new Message('new handler', $request->getMessage()->getData())); + $middleware2 = static function (Request $request, MessageHandlerInterface $handler): Request { + $request = $request->withMessage($request->getMessage()->withMetadata(['new' => 'metadata'])); - return $handler->handleFailure($request); + return $handler->handle($request); }; - $dispatcher = $this->createDispatcher()->withMiddlewares([FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [$middleware1, $middleware2]]); + $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('new test data', $request->getMessage()->getData()); - $this->assertSame('new handler', $request->getMessage()->getHandlerName()); + $this->assertSame(['new' => 'metadata'], $request->getMessage()->getMetadata()); } public function testMiddlewareStackInterrupted(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); - $middleware1 = static function (FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest { - return $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'first')); + $middleware1 = static function (Request $request, MessageHandlerInterface $handler): Request { + return $request->withMessage($request->getMessage()->withData('first')); }; - $middleware2 = static function (FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest { - return $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'second')); + $middleware2 = static function (Request $request, MessageHandlerInterface $handler): Request { + return $request->withMessage($request->getMessage()->withData('second')); }; - $dispatcher = $this->createDispatcher()->withMiddlewares([FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [$middleware1, $middleware2]]); + $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('first', $request->getMessage()->getData()); @@ -127,7 +120,7 @@ public function testImmutability(): void public function testResetStackOnWithMiddlewares(): void { - $request = $this->getFailureHandlingRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -140,16 +133,16 @@ public function testResetStackOnWithMiddlewares(): void ->withMiddlewares([[TestCallableMiddleware::class, 'index']]); $dispatcher->dispatch($request, $this->getRequestHandler()); - $dispatcher = $dispatcher->withMiddlewares([FailureMiddlewareDispatcher::DEFAULT_PIPELINE => [TestMiddleware::class]]); + $dispatcher = $dispatcher->withMiddlewares([TestMiddleware::class]); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); self::assertSame('New middleware test data', $request->getMessage()->getData()); } - private function getRequestHandler(): MessageFailureHandlerInterface + private function getRequestHandler(): MessageHandlerInterface { - return new class () implements MessageFailureHandlerInterface { - public function handleFailure(FailureHandlingRequest $request): FailureHandlingRequest + return new class () implements MessageHandlerInterface { + public function handle(Request $request): Request { return $request; } @@ -158,11 +151,11 @@ public function handleFailure(FailureHandlingRequest $request): FailureHandlingR private function createDispatcher( ContainerInterface $container = null, - ): FailureMiddlewareDispatcher { + ): MiddlewareDispatcher { $container ??= $this->createContainer([AdapterInterface::class => new FakeAdapter()]); $callableFactory = new CallableFactory($container); - return new FailureMiddlewareDispatcher(new MiddlewareFactoryFailure($container, $callableFactory), []); + return new MiddlewareDispatcher(new MiddlewareFactory($container, $callableFactory), []); } private function createContainer(array $instances = []): ContainerInterface @@ -170,11 +163,10 @@ private function createContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getFailureHandlingRequest(): FailureHandlingRequest + private function getRequest(): Request { - return new FailureHandlingRequest( - new Message('handler', 'data'), - new Exception('Test exception.'), + return new Request( + new Message('data'), $this->createMock(QueueInterface::class) ); } diff --git a/tests/Unit/Middleware/FailureHandling/MiddlewareFactoryTest.php b/tests/Unit/Middleware/FailureHandling/MiddlewareFactoryTest.php index 571ca037..4b02f0e8 100644 --- a/tests/Unit/Middleware/FailureHandling/MiddlewareFactoryTest.php +++ b/tests/Unit/Middleware/FailureHandling/MiddlewareFactoryTest.php @@ -4,22 +4,23 @@ namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling; -use Exception; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use RuntimeException; +use Throwable; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\Request; +use Yiisoft\Queue\QueueInterface; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailure; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFailureInterface; use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; -use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeAdapter; use Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Support\InvalidController; use Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Support\TestCallableMiddleware; @@ -30,19 +31,19 @@ final class MiddlewareFactoryTest extends TestCase public function testCreateFromString(): void { $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware(TestMiddleware::class); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(TestMiddleware::class); self::assertInstanceOf(TestMiddleware::class, $middleware); } public function testCreateCallableFromArray(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware([TestCallableMiddleware::class, 'index']); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware([TestCallableMiddleware::class, 'index']); self::assertSame( 'New test data', - $middleware->processFailure( + $middleware->process( $this->getConsumeRequest(), - $this->createMock(MessageFailureHandlerInterface::class) + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData(), ); } @@ -50,20 +51,19 @@ public function testCreateCallableFromArray(): void public function testCreateFromClosureResponse(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( function (): FailureHandlingRequest { return new FailureHandlingRequest( - new Message('test', 'test data'), + new Message('test data'), new RuntimeException('test exception'), - $this->createMock(QueueInterface::class), ); } ); self::assertSame( 'test data', - $middleware->processFailure( + $middleware->process( $this->getConsumeRequest(), - $this->createMock(MessageFailureHandlerInterface::class) + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData() ); } @@ -71,16 +71,16 @@ function (): FailureHandlingRequest { public function testCreateFromClosureMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware( - static function (): MiddlewareFailureInterface { + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( + static function (): MiddlewareInterface { return new TestMiddleware(); } ); self::assertSame( 'New middleware test data', - $middleware->processFailure( + $middleware->process( $this->getConsumeRequest(), - $this->createMock(MessageFailureHandlerInterface::class) + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData() ); } @@ -88,13 +88,13 @@ static function (): MiddlewareFailureInterface { public function testCreateWithUseParamsMiddleware(): void { $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware(TestMiddleware::class); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(TestMiddleware::class); self::assertSame( 'New middleware test data', - $middleware->processFailure( + $middleware->process( $this->getConsumeRequest(), - $this->getRequestHandler() + $this->getRequestHandler(new RuntimeException()) )->getMessage()->getData() ); } @@ -102,16 +102,16 @@ public function testCreateWithUseParamsMiddleware(): void public function testCreateWithTestCallableMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [TestCallableMiddleware::class, 'index'] ); $request = $this->getConsumeRequest(); self::assertSame( 'New test data', - $middleware->processFailure( + $middleware->process( $request, - $this->getRequestHandler() + $this->getRequestHandler(new RuntimeException()) )->getMessage()->getData() ); } @@ -134,28 +134,28 @@ public static function invalidMiddlewareDefinitionProvider(): array public function testInvalidMiddleware(mixed $definition): void { $this->expectException(InvalidMiddlewareDefinitionException::class); - $this->getMiddlewareFactory()->createFailureMiddleware($definition); + $this->getMiddlewareFactory()->createMiddleware($definition); } public function testInvalidMiddlewareWithWrongController(): void { $container = $this->getContainer([InvalidController::class => new InvalidController()]); - $middleware = $this->getMiddlewareFactory($container)->createFailureMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [InvalidController::class, 'index'] ); $this->expectException(InvalidMiddlewareDefinitionException::class); - $middleware->processFailure( + $middleware->process( $this->getConsumeRequest(), - $this->createMock(MessageFailureHandlerInterface::class) + $this->createMock(MessageHandlerInterface::class) ); } - private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryFailureInterface + private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryInterface { $container ??= $this->getContainer([AdapterInterface::class => new FakeAdapter()]); - return new MiddlewareFactoryFailure($container, new CallableFactory($container)); + return new MiddlewareFactory($container, new CallableFactory($container)); } private function getContainer(array $instances = []): ContainerInterface @@ -163,21 +163,24 @@ private function getContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getRequestHandler(): MessageFailureHandlerInterface + private function getRequestHandler(Throwable $e): MessageHandlerInterface { - return new class () implements MessageFailureHandlerInterface { - public function handleFailure(FailureHandlingRequest $request): FailureHandlingRequest + return new class ($e) implements MessageHandlerInterface { + public function __construct(private Throwable $e) + { + } + + public function handle(Request $request): Request { - throw $request->getException(); + throw $this->e; } }; } - private function getConsumeRequest(): FailureHandlingRequest + private function getConsumeRequest(): Request { - return new FailureHandlingRequest( - new Message('handler', 'data'), - new Exception('test exception'), + return new Request( + new Message('data'), $this->createMock(QueueInterface::class) ); } diff --git a/tests/Unit/Middleware/FailureHandling/Support/TestCallableMiddleware.php b/tests/Unit/Middleware/FailureHandling/Support/TestCallableMiddleware.php index 16dc135a..a11505a7 100644 --- a/tests/Unit/Middleware/FailureHandling/Support/TestCallableMiddleware.php +++ b/tests/Unit/Middleware/FailureHandling/Support/TestCallableMiddleware.php @@ -5,12 +5,12 @@ namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Support; use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; +use Yiisoft\Queue\Middleware\Request; final class TestCallableMiddleware { - public function index(FailureHandlingRequest $request): FailureHandlingRequest + public function index(Request $request): Request { - return $request->withMessage(new Message('test', 'New test data')); + return $request->withMessage(new Message('New test data')); } } diff --git a/tests/Unit/Middleware/FailureHandling/Support/TestMiddleware.php b/tests/Unit/Middleware/FailureHandling/Support/TestMiddleware.php index e929e357..5c43df12 100644 --- a/tests/Unit/Middleware/FailureHandling/Support/TestMiddleware.php +++ b/tests/Unit/Middleware/FailureHandling/Support/TestMiddleware.php @@ -5,18 +5,18 @@ namespace Yiisoft\Queue\Tests\Unit\Middleware\FailureHandling\Support; use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest; -use Yiisoft\Queue\Middleware\FailureHandling\MessageFailureHandlerInterface; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFailureInterface; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\Request; -final class TestMiddleware implements MiddlewareFailureInterface +final class TestMiddleware implements MiddlewareInterface { public function __construct(private string $message = 'New middleware test data') { } - public function processFailure(FailureHandlingRequest $request, MessageFailureHandlerInterface $handler): FailureHandlingRequest + public function process(Request $request, MessageHandlerInterface $handler): Request { - return $request->withMessage(new Message('test', $this->message)); + return $request->withMessage(new Message($this->message)); } } diff --git a/tests/Unit/Middleware/Push/InvalidMiddlewareDefinitionExceptionTest.php b/tests/Unit/Middleware/InvalidMiddlewareDefinitionExceptionTest.php similarity index 82% rename from tests/Unit/Middleware/Push/InvalidMiddlewareDefinitionExceptionTest.php rename to tests/Unit/Middleware/InvalidMiddlewareDefinitionExceptionTest.php index 9cb940cf..408196e9 100644 --- a/tests/Unit/Middleware/Push/InvalidMiddlewareDefinitionExceptionTest.php +++ b/tests/Unit/Middleware/InvalidMiddlewareDefinitionExceptionTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\Push; +namespace Yiisoft\Queue\Tests\Unit\Middleware; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use stdClass; use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; final class InvalidMiddlewareDefinitionExceptionTest extends TestCase { @@ -21,15 +21,15 @@ public static function dataBase(): array ], [ new TestCallableMiddleware(), - 'an instance of "Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware"', + 'an instance of "Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware"', ], [ [TestCallableMiddleware::class, 'notExistsAction'], - '["Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware", "notExistsAction"]', + '["Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware", "notExistsAction"]', ], [ ['class' => TestCallableMiddleware::class, 'index'], - '["class" => "Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware", "index"]', + '["class" => "Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware", "index"]', ], ]; } diff --git a/tests/Unit/Middleware/Push/MiddlewareDispatcherTest.php b/tests/Unit/Middleware/MiddlewareDispatcherTest.php similarity index 60% rename from tests/Unit/Middleware/Push/MiddlewareDispatcherTest.php rename to tests/Unit/Middleware/MiddlewareDispatcherTest.php index 713bab4a..ea7f5a2c 100644 --- a/tests/Unit/Middleware/Push/MiddlewareDispatcherTest.php +++ b/tests/Unit/Middleware/MiddlewareDispatcherTest.php @@ -2,34 +2,41 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\Push; +namespace Yiisoft\Queue\Tests\Unit\Middleware; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Yiisoft\Queue\Tests\App\FakeQueue; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; -use Yiisoft\Queue\Middleware\Push\MessageHandlerPushInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPush; -use Yiisoft\Queue\Middleware\Push\PushMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Push\PushRequest; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\Tests\App\FakeAdapter; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestMiddleware; final class MiddlewareDispatcherTest extends TestCase { public function testCallableMiddlewareCalled(): void { - $request = $this->getPushRequest(); + $request = $this->getRequest(); $dispatcher = $this->createDispatcher()->withMiddlewares( [ - static fn (PushRequest $request, AdapterInterface $adapter): PushRequest => $request - ->withMessage(new Message('test', 'New closure test data')) - ->withAdapter($adapter->withChannel('closure-channel')), + static fn (Request $request): Request => $request + ->withMessage(new Message('New closure test data')) + ->withQueue( + $request->getQueue()->getAdapter() === null + ? $request->getQueue() + : $request->getQueue()->withAdapter( + $request->getQueue()->getAdapter()->withChannel('closure-channel') + ) + ), ] ); @@ -39,12 +46,12 @@ public function testCallableMiddlewareCalled(): void * @psalm-suppress NoInterfaceProperties * @psalm-suppress PossiblyNullPropertyFetch */ - $this->assertSame('closure-channel', $request->getAdapter()->channel); + $this->assertSame('closure-channel', $request->getQueue()->getAdapter()->channel); } public function testArrayMiddlewareCallableDefinition(): void { - $request = $this->getPushRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -57,7 +64,7 @@ public function testArrayMiddlewareCallableDefinition(): void public function testFactoryArrayDefinition(): void { - $request = $this->getPushRequest(); + $request = $this->getRequest(); $container = $this->createContainer(); $definition = [ 'class' => TestMiddleware::class, @@ -70,41 +77,50 @@ public function testFactoryArrayDefinition(): void public function testMiddlewareFullStackCalled(): void { - $request = $this->getPushRequest(); + $middleware1 = static function (Request $request, MessageHandlerInterface $handler): Request { + $request = $request->withMessage($request->getMessage()->withData('new test data')); - $middleware1 = static function (PushRequest $request, MessageHandlerPushInterface $handler): PushRequest { - $request = $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'new test data')); - - return $handler->handlePush($request); + return $handler->handle($request); }; - $middleware2 = static function (PushRequest $request, MessageHandlerPushInterface $handler): PushRequest { + $middleware2 = static function (Request $request, MessageHandlerInterface $handler): Request { /** * @noinspection NullPointerExceptionInspection * * @psalm-suppress PossiblyNullReference */ - $request = $request->withAdapter($request->getAdapter()->withChannel('new channel')); + $queue = $request->getQueue(); + if ($queue !== null && $queue->getAdapter() !== null) { + $request = $request->withQueue( + $queue->withAdapter( + $queue->getAdapter()->withChannel('new channel') + ) + ); + } - return $handler->handlePush($request); + return $handler->handle($request); }; $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); + $request = $this->getRequest(); $request = $dispatcher->dispatch($request, $this->getRequestHandler()); $this->assertSame('new test data', $request->getMessage()->getData()); /** * @psalm-suppress NoInterfaceProperties * @psalm-suppress PossiblyNullPropertyFetch */ - $this->assertSame('new channel', $request->getAdapter()->channel); + $this->assertNotNull($request->getQueue()); + $this->assertNotNull($request->getQueue()->getAdapter()); + $this->assertInstanceOf(FakeAdapter::class, $request->getQueue()->getAdapter()); + $this->assertSame('new channel', $request->getQueue()->getAdapter()->channel); } public function testMiddlewareStackInterrupted(): void { - $request = $this->getPushRequest(); + $request = $this->getRequest(); - $middleware1 = static fn (PushRequest $request, MessageHandlerPushInterface $handler): PushRequest => $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'first')); - $middleware2 = static fn (PushRequest $request, MessageHandlerPushInterface $handler): PushRequest => $request->withMessage(new Message($request->getMessage()->getHandlerName(), 'second')); + $middleware1 = static fn (Request $request, MessageHandlerInterface $handler): Request => $request->withMessage($request->getMessage()->withData('first')); + $middleware2 = static fn (Request $request, MessageHandlerInterface $handler): Request => $request->withMessage($request->getMessage()->withData('second')); $dispatcher = $this->createDispatcher()->withMiddlewares([$middleware1, $middleware2]); @@ -137,7 +153,7 @@ public function testImmutability(): void public function testResetStackOnWithMiddlewares(): void { - $request = $this->getPushRequest(); + $request = $this->getRequest(); $container = $this->createContainer( [ TestCallableMiddleware::class => new TestCallableMiddleware(), @@ -156,10 +172,10 @@ public function testResetStackOnWithMiddlewares(): void self::assertSame('New middleware test data', $request->getMessage()->getData()); } - private function getRequestHandler(): MessageHandlerPushInterface + private function getRequestHandler(): MessageHandlerInterface { - return new class () implements MessageHandlerPushInterface { - public function handlePush(PushRequest $request): PushRequest + return new class () implements MessageHandlerInterface { + public function handle(Request $request): Request { return $request; } @@ -168,12 +184,12 @@ public function handlePush(PushRequest $request): PushRequest private function createDispatcher( ContainerInterface $container = null, - ): PushMiddlewareDispatcher { + ): MiddlewareDispatcher { $container ??= $this->createContainer([AdapterInterface::class => new FakeAdapter()]); $callableFactory = new CallableFactory($container); - return new PushMiddlewareDispatcher( - new MiddlewareFactoryPush($container, $callableFactory), + return new MiddlewareDispatcher( + new MiddlewareFactory($container, $callableFactory), ); } @@ -182,8 +198,9 @@ private function createContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getPushRequest(): PushRequest + private function getRequest(): Request { - return new PushRequest(new Message('handler', 'data'), new FakeAdapter()); + $queue = new FakeQueue('chan1'); + return new Request(new Message('data'), $queue->withAdapter(new FakeAdapter())); } } diff --git a/tests/Unit/Middleware/Push/MiddlewareFactoryTest.php b/tests/Unit/Middleware/MiddlewareFactoryTest.php similarity index 65% rename from tests/Unit/Middleware/Push/MiddlewareFactoryTest.php rename to tests/Unit/Middleware/MiddlewareFactoryTest.php index c68bc6db..469daec6 100644 --- a/tests/Unit/Middleware/Push/MiddlewareFactoryTest.php +++ b/tests/Unit/Middleware/MiddlewareFactoryTest.php @@ -2,44 +2,45 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\Push; +namespace Yiisoft\Queue\Tests\Unit\Middleware; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Yiisoft\Queue\QueueInterface; use Yiisoft\Test\Support\Container\SimpleContainer; use Yiisoft\Queue\Adapter\AdapterInterface; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Middleware\CallableFactory; use Yiisoft\Queue\Middleware\InvalidMiddlewareDefinitionException; -use Yiisoft\Queue\Middleware\Push\MessageHandlerPushInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPush; -use Yiisoft\Queue\Middleware\Push\MiddlewareFactoryPushInterface; -use Yiisoft\Queue\Middleware\Push\MiddlewarePushInterface; -use Yiisoft\Queue\Middleware\Push\PushRequest; +use Yiisoft\Queue\Middleware\MessageHandlerInterface; +use Yiisoft\Queue\Middleware\MiddlewareFactory; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; +use Yiisoft\Queue\Middleware\MiddlewareInterface; +use Yiisoft\Queue\Middleware\Request; use Yiisoft\Queue\Tests\App\FakeAdapter; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\InvalidController; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestCallableMiddleware; -use Yiisoft\Queue\Tests\Unit\Middleware\Push\Support\TestMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\InvalidController; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestCallableMiddleware; +use Yiisoft\Queue\Tests\Unit\Middleware\Support\TestMiddleware; final class MiddlewareFactoryTest extends TestCase { public function testCreateFromString(): void { $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware(TestMiddleware::class); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(TestMiddleware::class); self::assertInstanceOf(TestMiddleware::class, $middleware); } public function testCreateCallableFromArray(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware([TestCallableMiddleware::class, 'index']); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware([TestCallableMiddleware::class, 'index']); self::assertSame( 'New test data', - $middleware->processPush( - $this->getPushRequest(), - $this->createMock(MessageHandlerPushInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData(), ); } @@ -47,16 +48,17 @@ public function testCreateCallableFromArray(): void public function testCreateFromClosureResponse(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware( - static function (): PushRequest { - return new PushRequest(new Message('test', 'test data'), new FakeAdapter()); + $queue = $this->createMock(QueueInterface::class); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( + static function () use ($queue): Request { + return new Request(new Message('test data'), $queue); } ); self::assertSame( 'test data', - $middleware->processPush( - $this->getPushRequest(), - $this->createMock(MessageHandlerPushInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData() ); } @@ -64,16 +66,16 @@ static function (): PushRequest { public function testCreateFromClosureMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware( - static function (): MiddlewarePushInterface { + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( + static function (): MiddlewareInterface { return new TestMiddleware(); } ); self::assertSame( 'New middleware test data', - $middleware->processPush( - $this->getPushRequest(), - $this->createMock(MessageHandlerPushInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) )->getMessage()->getData() ); } @@ -81,12 +83,12 @@ static function (): MiddlewarePushInterface { public function testCreateWithUseParamsMiddleware(): void { $container = $this->getContainer([TestMiddleware::class => new TestMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware(TestMiddleware::class); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware(TestMiddleware::class); self::assertSame( 'New middleware test data', - $middleware->processPush( - $this->getPushRequest(), + $middleware->process( + $this->getRequest(), $this->getRequestHandler() )->getMessage()->getData() ); @@ -95,12 +97,12 @@ public function testCreateWithUseParamsMiddleware(): void public function testCreateWithTestCallableMiddleware(): void { $container = $this->getContainer([TestCallableMiddleware::class => new TestCallableMiddleware()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware([TestCallableMiddleware::class, 'index']); - $request = $this->getPushRequest(); + $middleware = $this->getMiddlewareFactory($container)->createMiddleware([TestCallableMiddleware::class, 'index']); + $request = $this->getRequest(); self::assertSame( 'New test data', - $middleware->processPush( + $middleware->process( $request, $this->getRequestHandler() )->getMessage()->getData() @@ -125,28 +127,28 @@ public static function invalidMiddlewareDefinitionProvider(): array public function testInvalidMiddleware(mixed $definition): void { $this->expectException(InvalidMiddlewareDefinitionException::class); - $this->getMiddlewareFactory()->createPushMiddleware($definition); + $this->getMiddlewareFactory()->createMiddleware($definition); } public function testInvalidMiddlewareWithWrongController(): void { $container = $this->getContainer([InvalidController::class => new InvalidController()]); - $middleware = $this->getMiddlewareFactory($container)->createPushMiddleware( + $middleware = $this->getMiddlewareFactory($container)->createMiddleware( [InvalidController::class, 'index'] ); $this->expectException(InvalidMiddlewareDefinitionException::class); - $middleware->processPush( - $this->getPushRequest(), - $this->createMock(MessageHandlerPushInterface::class) + $middleware->process( + $this->getRequest(), + $this->createMock(MessageHandlerInterface::class) ); } - private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryPushInterface + private function getMiddlewareFactory(ContainerInterface $container = null): MiddlewareFactoryInterface { $container ??= $this->getContainer([AdapterInterface::class => new FakeAdapter()]); - return new MiddlewareFactoryPush($container, new CallableFactory($container)); + return new MiddlewareFactory($container, new CallableFactory($container)); } private function getContainer(array $instances = []): ContainerInterface @@ -154,18 +156,18 @@ private function getContainer(array $instances = []): ContainerInterface return new SimpleContainer($instances); } - private function getRequestHandler(): MessageHandlerPushInterface + private function getRequestHandler(): MessageHandlerInterface { - return new class () implements MessageHandlerPushInterface { - public function handlePush(PushRequest $request): PushRequest + return new class () implements MessageHandlerInterface { + public function handle(Request $request): Request { return $request; } }; } - private function getPushRequest(): PushRequest + private function getRequest(): Request { - return new PushRequest(new Message('handler', 'data'), new FakeAdapter()); + return new Request(new Message('data'), $this->createMock(QueueInterface::class)); } } diff --git a/tests/Unit/Middleware/Push/PushRequestTest.php b/tests/Unit/Middleware/Push/PushRequestTest.php deleted file mode 100644 index 2c1533e5..00000000 --- a/tests/Unit/Middleware/Push/PushRequestTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertNotSame($pushRequest, $pushRequest->withAdapter(new FakeAdapter())); - $this->assertNotSame($pushRequest, $pushRequest->withMessage($message)); - } -} diff --git a/tests/Unit/Middleware/Push/Support/TestCallableMiddleware.php b/tests/Unit/Middleware/Push/Support/TestCallableMiddleware.php deleted file mode 100644 index 035ed999..00000000 --- a/tests/Unit/Middleware/Push/Support/TestCallableMiddleware.php +++ /dev/null @@ -1,16 +0,0 @@ -withMessage(new Message('test', 'New test data')); - } -} diff --git a/tests/Unit/Middleware/Push/Support/TestMiddleware.php b/tests/Unit/Middleware/Push/Support/TestMiddleware.php deleted file mode 100644 index a15c24f0..00000000 --- a/tests/Unit/Middleware/Push/Support/TestMiddleware.php +++ /dev/null @@ -1,22 +0,0 @@ -withMessage(new Message('test', $this->message)); - } -} diff --git a/tests/Unit/Middleware/RequestTest.php b/tests/Unit/Middleware/RequestTest.php new file mode 100644 index 00000000..96a62428 --- /dev/null +++ b/tests/Unit/Middleware/RequestTest.php @@ -0,0 +1,22 @@ +createMock(QueueInterface::class)); + + $this->assertNotSame($request, $request->withQueue($this->createMock(QueueInterface::class))); + $this->assertNotSame($request, $request->withMessage($message)); + } +} diff --git a/tests/Unit/Middleware/Push/Support/InvalidController.php b/tests/Unit/Middleware/Support/InvalidController.php similarity index 68% rename from tests/Unit/Middleware/Push/Support/InvalidController.php rename to tests/Unit/Middleware/Support/InvalidController.php index 95642cfb..14006ccf 100644 --- a/tests/Unit/Middleware/Push/Support/InvalidController.php +++ b/tests/Unit/Middleware/Support/InvalidController.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Queue\Tests\Unit\Middleware\Push\Support; +namespace Yiisoft\Queue\Tests\Unit\Middleware\Support; final class InvalidController { diff --git a/tests/Unit/Middleware/Support/TestCallableMiddleware.php b/tests/Unit/Middleware/Support/TestCallableMiddleware.php new file mode 100644 index 00000000..5009c084 --- /dev/null +++ b/tests/Unit/Middleware/Support/TestCallableMiddleware.php @@ -0,0 +1,16 @@ +withMessage(new Message('New test data')); + } +} diff --git a/tests/Unit/Middleware/Support/TestMiddleware.php b/tests/Unit/Middleware/Support/TestMiddleware.php new file mode 100644 index 00000000..e055480f --- /dev/null +++ b/tests/Unit/Middleware/Support/TestMiddleware.php @@ -0,0 +1,22 @@ +withMessage(new Message($this->message)); + } +} diff --git a/tests/Unit/QueueTest.php b/tests/Unit/QueueTest.php index 7a477e66..726e016e 100644 --- a/tests/Unit/QueueTest.php +++ b/tests/Unit/QueueTest.php @@ -4,27 +4,22 @@ namespace Yiisoft\Queue\Tests\Unit; +use Yiisoft\Queue\Tests\Support\StackMessage; use Yiisoft\Queue\Cli\SignalLoop; use Yiisoft\Queue\Exception\AdapterConfiguration\AdapterNotConfiguredException; +use Yiisoft\Queue\Message\JsonMessageSerializer; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\Tests\App\FakeAdapter; +use Yiisoft\Queue\Tests\Support\NullMessage; use Yiisoft\Queue\Tests\TestCase; use Yiisoft\Queue\Message\IdEnvelope; +use Yiisoft\Queue\Tests\Support\StackMessageHandler; final class QueueTest extends TestCase { - private bool $needsRealAdapter = true; - - protected function setUp(): void - { - parent::setUp(); - - $this->needsRealAdapter = true; - } - protected function needsRealAdapter(): bool { - return $this->needsRealAdapter; + return true; } public function testPushSuccessful(): void @@ -33,7 +28,7 @@ public function testPushSuccessful(): void $queue = $this ->getQueue() ->withAdapter($adapter); - $message = new Message('simple', null); + $message = new Message(null); $queue->push($message); self::assertSame([$message], $adapter->pushMessages); @@ -44,18 +39,25 @@ public function testRun(): void $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); - $message = new Message('simple', null); + $message = new StackMessage(); + $serializer = new JsonMessageSerializer(); + $message = $serializer->unserialize($serializer->serialize($message)); + $message2 = clone $message; $queue->push($message); $queue->push($message2); $queue->run(); - self::assertEquals(2, $this->executionTimes); + $stackMessageHandler = $this->container->get(StackMessageHandler::class); + self::assertCount(2, $stackMessageHandler->processedMessages); } public function testRunPartly(): void { - $message = new Message('simple', null); + $message = new StackMessage(null); + $serializer = new JsonMessageSerializer(); + $message = $serializer->unserialize($serializer->serialize($message)); + $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); @@ -64,7 +66,8 @@ public function testRunPartly(): void $queue->push($message2); $queue->run(1); - self::assertEquals(1, $this->executionTimes); + $stackMessageHandler = $this->container->get(StackMessageHandler::class); + self::assertCount(1, $stackMessageHandler->processedMessages); } public function testListen(): void @@ -72,13 +75,17 @@ public function testListen(): void $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); - $message = new Message('simple', null); + $message = new StackMessage(null); + $serializer = new JsonMessageSerializer(); + $message = $serializer->unserialize($serializer->serialize($message)); + $message2 = clone $message; $queue->push($message); $queue->push($message2); $queue->listen(); - self::assertEquals(2, $this->executionTimes); + $stackMessageHandler = $this->container->get(StackMessageHandler::class); + self::assertCount(2, $stackMessageHandler->processedMessages); } public function testStatus(): void @@ -86,7 +93,10 @@ public function testStatus(): void $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); - $message = new Message('simple', null); + $message = new NullMessage(null); + $serializer = new JsonMessageSerializer(); + $message = $serializer->unserialize($serializer->serialize($message)); + $envelope = $queue->push($message); self::assertArrayHasKey(IdEnvelope::MESSAGE_ID_KEY, $envelope->getMetadata()); @@ -107,7 +117,7 @@ public function testAdapterNotConfiguredException(): void { try { $queue = $this->getQueue(); - $message = new Message('simple', null); + $message = new Message(null); $envelope = $queue->push($message); $queue->status($envelope->getMetadata()[IdEnvelope::MESSAGE_ID_KEY]); } catch (AdapterNotConfiguredException $exception) { @@ -134,12 +144,16 @@ public function testRunWithSignalLoop(): void $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); - $message = new Message('simple', null); + $message = new StackMessage(null); + $serializer = new JsonMessageSerializer(); + $message = $serializer->unserialize($serializer->serialize($message)); + $message2 = clone $message; $queue->push($message); $queue->push($message2); $queue->run(); - self::assertEquals(2, $this->executionTimes); + $stackMessageHandler = $this->container->get(StackMessageHandler::class); + self::assertCount(2, $stackMessageHandler->processedMessages); } } diff --git a/tests/Unit/SynchronousAdapterTest.php b/tests/Unit/SynchronousAdapterTest.php index 31a43f50..771b2379 100644 --- a/tests/Unit/SynchronousAdapterTest.php +++ b/tests/Unit/SynchronousAdapterTest.php @@ -7,6 +7,7 @@ use Yiisoft\Queue\Enum\JobStatus; use Yiisoft\Queue\Message\Message; use Yiisoft\Queue\QueueFactory; +use Yiisoft\Queue\Tests\Support\NullMessage; use Yiisoft\Queue\Tests\TestCase; use Yiisoft\Queue\Message\IdEnvelope; @@ -22,7 +23,7 @@ public function testNonIntegerId(): void $queue = $this ->getQueue() ->withAdapter($this->getAdapter()); - $message = new Message('simple', null); + $message = new Message(null); $envelope = $queue->push($message); self::assertArrayHasKey(IdEnvelope::MESSAGE_ID_KEY, $envelope->getMetadata()); @@ -34,7 +35,7 @@ public function testNonIntegerId(): void public function testIdSetting(): void { - $message = new Message('simple', []); + $message = new NullMessage(); $adapter = $this->getAdapter(); $ids = []; @@ -57,7 +58,7 @@ public function testWithSameChannel(): void public function testWithAnotherChannel(): void { $adapter = $this->getAdapter(); - $adapter->push(new Message('test', null)); + $adapter->push(new Message(null)); $adapterNew = $adapter->withChannel('test'); self::assertNotEquals($adapter, $adapterNew); diff --git a/tests/Unit/WorkerTest.php b/tests/Unit/WorkerTest.php index d0fc9733..9b9c15d5 100644 --- a/tests/Unit/WorkerTest.php +++ b/tests/Unit/WorkerTest.php @@ -4,199 +4,88 @@ namespace Yiisoft\Queue\Tests\Unit; -use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use Yiisoft\Injector\Injector; -use Yiisoft\Test\Support\Container\SimpleContainer; -use Yiisoft\Test\Support\Log\SimpleLogger; +use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; +use Yiisoft\EventDispatcher\Provider\ListenerCollection; +use Yiisoft\EventDispatcher\Provider\Provider; use Yiisoft\Queue\Exception\JobFailureException; use Yiisoft\Queue\Message\Message; -use Yiisoft\Queue\Message\MessageInterface; -use Yiisoft\Queue\Middleware\Consume\ConsumeMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\Consume\MiddlewareFactoryConsumeInterface; -use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher; -use Yiisoft\Queue\Middleware\FailureHandling\MiddlewareFactoryFailureInterface; +use Yiisoft\Queue\Middleware\MiddlewareDispatcher; +use Yiisoft\Queue\Middleware\MiddlewareFactoryInterface; use Yiisoft\Queue\QueueInterface; use Yiisoft\Queue\Tests\App\FakeHandler; +use Yiisoft\Queue\Tests\Support\ExceptionMessage; +use Yiisoft\Queue\Tests\Support\ExceptionMessageHandler; +use Yiisoft\Queue\Tests\Support\StackMessage; +use Yiisoft\Queue\Tests\Support\StackMessageHandler; use Yiisoft\Queue\Tests\TestCase; use Yiisoft\Queue\Worker\Worker; +use Yiisoft\Test\Support\Log\SimpleLogger; final class WorkerTest extends TestCase { - public function testJobExecutedWithCallableHandler(): void - { - $handleMessage = null; - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $container = new SimpleContainer(); - $handlers = [ - 'simple' => function (MessageInterface $message) use (&$handleMessage) { - $handleMessage = $message; - }, - ]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); - - $worker->process($message, $queue); - $this->assertSame($message, $handleMessage); - - $messages = $logger->getMessages(); - $this->assertNotEmpty($messages); - $this->assertStringContainsString('Processing message #null.', $messages[0]['message']); - } - - public function testJobExecutedWithDefinitionHandler(): void - { - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => FakeHandler::class]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); - - $worker->process($message, $queue); - $this->assertSame([$message], $handler::$processedMessages); - } - public function testJobExecutedWithDefinitionClassHandler(): void { - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => [FakeHandler::class, 'execute']]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); + $message = new Message('data', ['test-meta-data']); - $worker->process($message, $queue); - $this->assertSame([$message], $handler::$processedMessages); - } - - public function testJobFailWithDefinitionNotFoundClassButExistInContainerHandler(): void - { - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); $handler = new FakeHandler(); - $container = new SimpleContainer(['not-found-class-name' => $handler]); - $handlers = ['simple' => ['not-found-class-name', 'execute']]; $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); + $worker = $this->createWorkerByParams(new SimpleLogger(), [Message::class => $handler]); $worker->process($message, $queue); - $this->assertSame([$message], $handler::$processedMessages); - } - public function testJobExecutedWithStaticDefinitionHandler(): void - { - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => FakeHandler::staticExecute(...)]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); - - $worker->process($message, $queue); $this->assertSame([$message], $handler::$processedMessages); } - public function testJobFailWithDefinitionUndefinedMethodHandler(): void + public function testHandlerIsReplacedWithEnvelopsOne(): void { - $this->expectExceptionMessage('Queue handler with name "simple" does not exist'); + $message = new StackMessage(['test-data']); - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => [FakeHandler::class, 'undefinedMethod']]; + $stackMessageHandler = new StackMessageHandler(); $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); + $worker = $this->createWorkerByParams( + new SimpleLogger(), + [StackMessage::class => fn ($message) => $stackMessageHandler->handle($message)] + ); $worker->process($message, $queue); + $this->assertSame([$message], $stackMessageHandler->processedMessages); } - public function testJobFailWithDefinitionUndefinedClassHandler(): void - { - $this->expectExceptionMessage('Queue handler with name "simple" does not exist'); - - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => ['UndefinedClass', 'handle']]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); - - try { - $worker->process($message, $queue); - } finally { - $messages = $logger->getMessages(); - $this->assertNotEmpty($messages); - $this->assertStringContainsString('UndefinedClass doesn\'t exist.', $messages[1]['message']); - } - } - - public function testJobFailWithDefinitionClassNotFoundInContainerHandler(): void + public function testJobFailWithDefinitionHandlerException(): void { - $this->expectExceptionMessage('Queue handler with name "simple" does not exist'); - $message = new Message('simple', ['test-data']); + $message = new ExceptionMessage(['test-data']); $logger = new SimpleLogger(); - $container = new SimpleContainer(); - $handlers = ['simple' => [FakeHandler::class, 'execute']]; $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); + $worker = $this->createWorkerByParams( + $logger, + [ExceptionMessage::class => fn ($message) => (new ExceptionMessageHandler())->handle($message)] + ); + $this->expectException(JobFailureException::class); + $this->expectExceptionMessage( + "Processing of message #null is stopped because of an exception:\nTest exception." + ); $worker->process($message, $queue); } - public function testJobFailWithDefinitionHandlerException(): void - { - $message = new Message('simple', ['test-data']); - $logger = new SimpleLogger(); - $handler = new FakeHandler(); - $container = new SimpleContainer([FakeHandler::class => $handler]); - $handlers = ['simple' => [FakeHandler::class, 'executeWithException']]; - - $queue = $this->createMock(QueueInterface::class); - $worker = $this->createWorkerByParams($handlers, $logger, $container); - - try { - $worker->process($message, $queue); - } catch (JobFailureException $exception) { - self::assertSame($exception::class, JobFailureException::class); - self::assertSame($exception->getMessage(), "Processing of message #null is stopped because of an exception:\nTest exception."); - self::assertEquals(['test-data'], $exception->getQueueMessage()->getData()); - } finally { - $messages = $logger->getMessages(); - $this->assertNotEmpty($messages); - $this->assertStringContainsString( - "Processing of message #null is stopped because of an exception:\nTest exception.", - $messages[1]['message'] - ); - } - } - private function createWorkerByParams( - array $handlers, LoggerInterface $logger, - ContainerInterface $container + array $listeners, ): Worker { + $collection = (new ListenerCollection()); + foreach ($listeners as $class => $listener) { + $collection = $collection->add($listener, $class); + } return new Worker( - $handlers, $logger, - new Injector($container), - $container, - new ConsumeMiddlewareDispatcher($this->createMock(MiddlewareFactoryConsumeInterface::class)), - new FailureMiddlewareDispatcher($this->createMock(MiddlewareFactoryFailureInterface::class), []), + new Dispatcher(new Provider($collection)), + $this->createContainer(), + new MiddlewareDispatcher($this->createMock(MiddlewareFactoryInterface::class)), + new MiddlewareDispatcher($this->createMock(MiddlewareFactoryInterface::class)), ); } }