mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-28 00:38:46 +03:00
Merge pull request #465 from acelaya/feature/external-event-dispatcher
Feature/external event dispatcher
This commit is contained in:
commit
a5c96f41b3
23 changed files with 3 additions and 851 deletions
|
@ -13,7 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
|||
#### Changed
|
||||
|
||||
* [#450](https://github.com/shlinkio/shlink/issues/450) Added PHP 7.4 to the build matrix, as an allowed-to-fail env.
|
||||
* [#443](https://github.com/shlinkio/shlink/issues/443) Split some logic into independent modules.
|
||||
* [#441](https://github.com/shlinkio/shlink/issues/441) and [#443](https://github.com/shlinkio/shlink/issues/443) Split some logic into independent modules.
|
||||
* [#451](https://github.com/shlinkio/shlink/issues/451) Updated to infection 0.13.
|
||||
|
||||
#### Deprecated
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"phly/phly-event-dispatcher": "^1.0",
|
||||
"predis/predis": "^1.1",
|
||||
"shlinkio/shlink-common": "^1.0",
|
||||
"shlinkio/shlink-event-dispatcher": "^1.0",
|
||||
"shlinkio/shlink-installer": "^1.2.1",
|
||||
"shlinkio/shlink-ip-geolocation": "^1.0",
|
||||
"symfony/console": "^4.3",
|
||||
|
@ -76,12 +77,8 @@
|
|||
"Shlinkio\\Shlink\\CLI\\": "module/CLI/src",
|
||||
"Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
|
||||
"Shlinkio\\Shlink\\Core\\": "module/Core/src",
|
||||
"Shlinkio\\Shlink\\EventDispatcher\\": "module/EventDispatcher/src",
|
||||
"Shlinkio\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/src/"
|
||||
},
|
||||
"files": [
|
||||
"module/EventDispatcher/functions/functions.php"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
@ -92,7 +89,6 @@
|
|||
"module/Core/test",
|
||||
"module/Core/test-db"
|
||||
],
|
||||
"ShlinkioTest\\Shlink\\EventDispatcher\\": "module/EventDispatcher/test",
|
||||
"ShlinkioTest\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/test"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Alejandro Celaya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,13 +0,0 @@
|
|||
# Shlink Event Dispatcher
|
||||
|
||||
This library provides a PSR-14 EventDispatcher which is capable of dispatching both regular listeners and async listeners which are run using [swoole]'s task system.
|
||||
|
||||
Most of the elements it provides require a [PSR-11] container, and it's easy to integrate on [expressive] applications thanks to the `ConfigProvider` it includes.
|
||||
|
||||
## Install
|
||||
|
||||
Install this library using composer:
|
||||
|
||||
composer require shlinkio/shlink-event-dispatcher
|
||||
|
||||
> This library is also an expressive module which provides its own `ConfigProvider`. Add it to your configuration to get everything automatically set up.
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher;
|
||||
|
||||
use Phly\EventDispatcher as Phly;
|
||||
use Psr\EventDispatcher as Psr;
|
||||
use Zend\ServiceManager\Proxy\LazyServiceFactory;
|
||||
|
||||
return [
|
||||
|
||||
'events' => [
|
||||
'regular' => [],
|
||||
'async' => [],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
Phly\EventDispatcher::class => Phly\EventDispatcherFactory::class,
|
||||
Psr\ListenerProviderInterface::class => Listener\ListenerProviderFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
Psr\EventDispatcherInterface::class => Phly\EventDispatcher::class,
|
||||
],
|
||||
'delegators' => [
|
||||
// The listener provider has to be lazy, because it uses the Swoole server to generate AsyncEventListeners
|
||||
// Without making this lazy, CLI commands which depend on the EventDispatcher fail
|
||||
Psr\ListenerProviderInterface::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
Psr\ListenerProviderInterface::class => Psr\ListenerProviderInterface::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher;
|
||||
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
Async\TaskRunner::class => Async\TaskRunnerFactory::class,
|
||||
],
|
||||
'delegators' => [
|
||||
HttpServer::class => [
|
||||
Async\TaskRunnerDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher;
|
||||
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
function asyncListener(HttpServer $server, string $regularListenerName): Listener\AsyncEventListener
|
||||
{
|
||||
return new Listener\AsyncEventListener($server, $regularListenerName);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Async;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
interface TaskInterface
|
||||
{
|
||||
public function run(ContainerInterface $container): void;
|
||||
|
||||
public function toString(): string;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Async;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
use Throwable;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function is_object;
|
||||
|
||||
class TaskRunner
|
||||
{
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
/** @var ContainerInterface */
|
||||
private $container;
|
||||
|
||||
public function __construct(LoggerInterface $logger, ContainerInterface $container)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function __invoke(HttpServer $server, int $taskId, int $fromId, $task): void
|
||||
{
|
||||
if (! $task instanceof TaskInterface) {
|
||||
$this->logger->warning('Invalid task provided to task worker: {type}. Task ignored', [
|
||||
'type' => is_object($task) ? get_class($task) : gettype($task),
|
||||
]);
|
||||
$server->finish('');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->notice('Starting work on task {taskId}: {task}', [
|
||||
'taskId' => $taskId,
|
||||
'task' => $task->toString(),
|
||||
]);
|
||||
|
||||
try {
|
||||
$task->run($this->container);
|
||||
} catch (Throwable $e) {
|
||||
$this->logger->error('Error processing task {taskId}: {e}', [
|
||||
'taskId' => $taskId,
|
||||
'e' => $e,
|
||||
]);
|
||||
} finally {
|
||||
$server->finish('');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Async;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
class TaskRunnerDelegator
|
||||
{
|
||||
public function __invoke(ContainerInterface $container, $name, callable $callback): HttpServer
|
||||
{
|
||||
/** @var HttpServer $server */
|
||||
$server = $callback();
|
||||
$logger = $container->get(LoggerInterface::class);
|
||||
|
||||
$server->on('task', $container->get(TaskRunner::class));
|
||||
$server->on('finish', function (HttpServer $server, int $taskId) use ($logger) {
|
||||
$logger->notice('Task #{taskId} has finished processing', ['taskId' => $taskId]);
|
||||
});
|
||||
|
||||
return $server;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Async;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class TaskRunnerFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container): TaskRunner
|
||||
{
|
||||
$logger = $container->get(LoggerInterface::class);
|
||||
return new TaskRunner($logger, $container);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher;
|
||||
|
||||
use function Shlinkio\Shlink\Common\loadConfigFromGlob;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return loadConfigFromGlob(__DIR__ . '/../config/{,*.}config.php');
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
class AsyncEventListener
|
||||
{
|
||||
/** @var string */
|
||||
private $regularListenerName;
|
||||
/** @var HttpServer */
|
||||
private $server;
|
||||
|
||||
public function __construct(HttpServer $server, string $regularListenerName)
|
||||
{
|
||||
$this->regularListenerName = $regularListenerName;
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
public function __invoke(object $event): void
|
||||
{
|
||||
$this->server->task(new EventListenerTask($this->regularListenerName, $event));
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskInterface;
|
||||
|
||||
use function get_class;
|
||||
use function sprintf;
|
||||
|
||||
class EventListenerTask implements TaskInterface
|
||||
{
|
||||
/** @var string */
|
||||
private $listenerName;
|
||||
/** @var object */
|
||||
private $event;
|
||||
|
||||
public function __construct(string $listenerName, object $event)
|
||||
{
|
||||
$this->listenerName = $listenerName;
|
||||
$this->event = $event;
|
||||
}
|
||||
|
||||
public function run(ContainerInterface $container): void
|
||||
{
|
||||
($container->get($this->listenerName))($this->event);
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return sprintf('Listener -> "%s", Event -> "%s"', $this->listenerName, get_class($this->event));
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use Phly\EventDispatcher\ListenerProvider\AttachableListenerProvider;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
use function Phly\EventDispatcher\lazyListener;
|
||||
use function Shlinkio\Shlink\EventDispatcher\asyncListener;
|
||||
|
||||
class ListenerProviderFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$config = $container->has('config') ? $container->get('config') : [];
|
||||
$events = $config['events'] ?? [];
|
||||
$provider = new AttachableListenerProvider();
|
||||
|
||||
$this->registerListeners($events['regular'] ?? [], $container, $provider);
|
||||
$this->registerListeners($events['async'] ?? [], $container, $provider, true);
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
private function registerListeners(
|
||||
array $events,
|
||||
ContainerInterface $container,
|
||||
AttachableListenerProvider $provider,
|
||||
bool $isAsync = false
|
||||
): void {
|
||||
if (empty($events)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid registering async event listeners when the swoole server is not registered
|
||||
if ($isAsync && ! $container->has(HttpServer::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($events as $eventName => $listeners) {
|
||||
foreach ($listeners as $listenerName) {
|
||||
$eventListener = $isAsync
|
||||
? asyncListener($container->get(HttpServer::class), $listenerName)
|
||||
: lazyListener($container, $listenerName);
|
||||
|
||||
$provider->listen($eventName, $eventListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunnerDelegator;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
class TaskRunnerDelegatorTest extends TestCase
|
||||
{
|
||||
/** @var TaskRunnerDelegator */
|
||||
private $delegator;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->delegator = new TaskRunnerDelegator();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function serverIsFetchedFromCallbackAndDecorated(): void
|
||||
{
|
||||
$server = $this->createMock(HttpServer::class);
|
||||
$server
|
||||
->expects($this->exactly(2))
|
||||
->method('on');
|
||||
$callback = function () use ($server) {
|
||||
return $server;
|
||||
};
|
||||
|
||||
$container = $this->prophesize(ContainerInterface::class);
|
||||
$getTaskRunner = $container->get(TaskRunner::class)->willReturn($this->prophesize(TaskRunner::class)->reveal());
|
||||
$getLogger = $container->get(LoggerInterface::class)->willReturn(
|
||||
$this->prophesize(LoggerInterface::class)->reveal()
|
||||
);
|
||||
|
||||
$result = ($this->delegator)($container->reveal(), '', $callback);
|
||||
|
||||
$this->assertSame($server, $result);
|
||||
$getTaskRunner->shouldHaveBeenCalledOnce();
|
||||
$getLogger->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ReflectionObject;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunnerFactory;
|
||||
|
||||
class TaskRunnerFactoryTest extends TestCase
|
||||
{
|
||||
/** @var TaskRunnerFactory */
|
||||
private $factory;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->factory = new TaskRunnerFactory();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function properlyCreatesService(): void
|
||||
{
|
||||
$loggerMock = $this->prophesize(LoggerInterface::class);
|
||||
$logger = $loggerMock->reveal();
|
||||
$containerMock = $this->prophesize(ContainerInterface::class);
|
||||
$getLogger = $containerMock->get(LoggerInterface::class)->willReturn($logger);
|
||||
$container = $containerMock->reveal();
|
||||
|
||||
$taskRunner = ($this->factory)($container, '');
|
||||
$loggerProp = $this->getPropertyFromTaskRunner($taskRunner, 'logger');
|
||||
$containerProp = $this->getPropertyFromTaskRunner($taskRunner, 'container');
|
||||
|
||||
$this->assertSame($container, $containerProp);
|
||||
$this->assertSame($logger, $loggerProp);
|
||||
$getLogger->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
|
||||
private function getPropertyFromTaskRunner(TaskRunner $taskRunner, string $propertyName)
|
||||
{
|
||||
$ref = new ReflectionObject($taskRunner);
|
||||
$prop = $ref->getProperty($propertyName);
|
||||
$prop->setAccessible(true);
|
||||
return $prop->getValue($taskRunner);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Async;
|
||||
|
||||
use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\Async\TaskRunner;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
class TaskRunnerTest extends TestCase
|
||||
{
|
||||
/** @var TaskRunner */
|
||||
private $taskRunner;
|
||||
/** @var ObjectProphecy */
|
||||
private $logger;
|
||||
/** @var ObjectProphecy */
|
||||
private $container;
|
||||
/** @var HttpServer */
|
||||
private $server;
|
||||
/** @var ObjectProphecy */
|
||||
private $task;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->logger = $this->prophesize(LoggerInterface::class);
|
||||
$this->container = $this->prophesize(ContainerInterface::class);
|
||||
$this->task = $this->prophesize(TaskInterface::class);
|
||||
|
||||
$this->server = $this->createMock(HttpServer::class);
|
||||
$this->server
|
||||
->expects($this->once())
|
||||
->method('finish')
|
||||
->with('');
|
||||
|
||||
$this->taskRunner = new TaskRunner($this->logger->reveal(), $this->container->reveal());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function warningIsLoggedWhenProvidedTaskIsInvalid(): void
|
||||
{
|
||||
$logWarning = $this->logger->warning('Invalid task provided to task worker: {type}. Task ignored', [
|
||||
'type' => 'string',
|
||||
]);
|
||||
$logInfo = $this->logger->info(Argument::cetera());
|
||||
$logError = $this->logger->error(Argument::cetera());
|
||||
|
||||
($this->taskRunner)($this->server, 1, 1, 'invalid_task');
|
||||
|
||||
$logWarning->shouldHaveBeenCalledOnce();
|
||||
$logInfo->shouldNotHaveBeenCalled();
|
||||
$logError->shouldNotHaveBeenCalled();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function properTasksAreRun(): void
|
||||
{
|
||||
$logWarning = $this->logger->warning(Argument::cetera());
|
||||
$logInfo = $this->logger->notice('Starting work on task {taskId}: {task}', [
|
||||
'taskId' => 1,
|
||||
'task' => 'The task',
|
||||
]);
|
||||
$logError = $this->logger->error(Argument::cetera());
|
||||
$taskToString = $this->task->toString()->willReturn('The task');
|
||||
$taskRun = $this->task->run($this->container->reveal())->will(function () {
|
||||
});
|
||||
|
||||
($this->taskRunner)($this->server, 1, 1, $this->task->reveal());
|
||||
|
||||
$logWarning->shouldNotHaveBeenCalled();
|
||||
$logInfo->shouldHaveBeenCalledOnce();
|
||||
$logError->shouldNotHaveBeenCalled();
|
||||
$taskToString->shouldHaveBeenCalledOnce();
|
||||
$taskRun->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function errorIsLoggedWhenTasksFail(): void
|
||||
{
|
||||
$e = new Exception('Error');
|
||||
|
||||
$logWarning = $this->logger->warning(Argument::cetera());
|
||||
$logInfo = $this->logger->notice('Starting work on task {taskId}: {task}', [
|
||||
'taskId' => 1,
|
||||
'task' => 'The task',
|
||||
]);
|
||||
$logError = $this->logger->error('Error processing task {taskId}: {e}', [
|
||||
'taskId' => 1,
|
||||
'e' => $e,
|
||||
]);
|
||||
$taskToString = $this->task->toString()->willReturn('The task');
|
||||
$taskRun = $this->task->run($this->container->reveal())->willThrow($e);
|
||||
|
||||
($this->taskRunner)($this->server, 1, 1, $this->task->reveal());
|
||||
|
||||
$logWarning->shouldNotHaveBeenCalled();
|
||||
$logInfo->shouldHaveBeenCalledOnce();
|
||||
$logError->shouldHaveBeenCalledOnce();
|
||||
$taskToString->shouldHaveBeenCalledOnce();
|
||||
$taskRun->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\EventDispatcher\ConfigProvider;
|
||||
|
||||
class ConfigProviderTest extends TestCase
|
||||
{
|
||||
/** @var ConfigProvider */
|
||||
private $configProvider;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->configProvider = new ConfigProvider();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function configIsReturned(): void
|
||||
{
|
||||
$config = $this->configProvider->__invoke();
|
||||
|
||||
$this->assertArrayHasKey('dependencies', $config);
|
||||
$this->assertArrayHasKey('events', $config);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\EventDispatcher\Listener\AsyncEventListener;
|
||||
use Shlinkio\Shlink\EventDispatcher\Listener\EventListenerTask;
|
||||
use stdClass;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
class AsyncEventListenerTest extends TestCase
|
||||
{
|
||||
/** @var AsyncEventListener */
|
||||
private $eventListener;
|
||||
/** @var HttpServer */
|
||||
private $server;
|
||||
/** @var string */
|
||||
private $regularListenerName;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->regularListenerName = 'the_regular_listener';
|
||||
$this->server = $this->createMock(HttpServer::class);
|
||||
|
||||
$this->eventListener = new AsyncEventListener($this->server, $this->regularListenerName);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function enqueuesTaskWhenInvoked(): void
|
||||
{
|
||||
$event = new stdClass();
|
||||
|
||||
$this->server
|
||||
->expects($this->once())
|
||||
->method('task')
|
||||
->with(new EventListenerTask($this->regularListenerName, $event));
|
||||
|
||||
($this->eventListener)($event);
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\Listener\EventListenerTask;
|
||||
use stdClass;
|
||||
|
||||
use function get_class;
|
||||
use function sprintf;
|
||||
|
||||
class EventListenerTaskTest extends TestCase
|
||||
{
|
||||
/** @var EventListenerTask */
|
||||
private $task;
|
||||
/** @var object */
|
||||
private $event;
|
||||
/** @var string */
|
||||
private $listenerName;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->event = new stdClass();
|
||||
$this->listenerName = 'the_listener';
|
||||
|
||||
$this->task = new EventListenerTask($this->listenerName, $this->event);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function toStringReturnsTheStringRepresentation(): void
|
||||
{
|
||||
$this->assertEquals(
|
||||
sprintf('Listener -> "%s", Event -> "%s"', $this->listenerName, get_class($this->event)),
|
||||
$this->task->toString()
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function runInvokesContainerAndListenerWithEvent(): void
|
||||
{
|
||||
$invoked = false;
|
||||
$container = $this->prophesize(ContainerInterface::class);
|
||||
$listener = function (object $event) use (&$invoked) {
|
||||
$invoked = true;
|
||||
Assert::assertSame($event, $this->event);
|
||||
};
|
||||
|
||||
$getListener = $container->get($this->listenerName)->willReturn($listener);
|
||||
|
||||
$this->task->run($container->reveal());
|
||||
|
||||
$this->assertTrue($invoked);
|
||||
$getListener->shouldHaveBeenCalledOnce();
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink\EventDispatcher\Listener;
|
||||
|
||||
use Phly\EventDispatcher\ListenerProvider\AttachableListenerProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use ReflectionObject;
|
||||
use Shlinkio\Shlink\EventDispatcher\Listener\ListenerProviderFactory;
|
||||
use Swoole\Http\Server as HttpServer;
|
||||
|
||||
use function Phly\EventDispatcher\lazyListener;
|
||||
use function Shlinkio\Shlink\EventDispatcher\asyncListener;
|
||||
|
||||
class ListenerProviderFactoryTest extends TestCase
|
||||
{
|
||||
/** @var ListenerProviderFactory */
|
||||
private $factory;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->factory = new ListenerProviderFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @dataProvider provideContainersWithoutEvents
|
||||
*/
|
||||
public function noListenersAreAttachedWhenNoConfigOrEventsAreRegistered(ContainerInterface $container): void
|
||||
{
|
||||
$provider = ($this->factory)($container, '');
|
||||
$listeners = $this->getListenersFromProvider($provider);
|
||||
|
||||
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
|
||||
$this->assertEmpty($listeners);
|
||||
}
|
||||
|
||||
public function provideContainersWithoutEvents(): iterable
|
||||
{
|
||||
yield 'no config' => [(function () {
|
||||
$container = $this->prophesize(ContainerInterface::class);
|
||||
$container->has('config')->willReturn(false);
|
||||
|
||||
return $container->reveal();
|
||||
})()];
|
||||
yield 'no events' => [(function () {
|
||||
$container = $this->prophesize(ContainerInterface::class);
|
||||
$container->has('config')->willReturn(true);
|
||||
$container->get('config')->willReturn([]);
|
||||
|
||||
return $container->reveal();
|
||||
})()];
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function configuredRegularEventsAreProperlyAttached(): void
|
||||
{
|
||||
$containerMock = $this->prophesize(ContainerInterface::class);
|
||||
$containerMock->has('config')->willReturn(true);
|
||||
$containerMock->get('config')->willReturn([
|
||||
'events' => [
|
||||
'regular' => [
|
||||
'foo' => [
|
||||
'bar',
|
||||
'baz',
|
||||
],
|
||||
'something' => [
|
||||
'some_listener',
|
||||
'another_listener',
|
||||
'foobar',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$container = $containerMock->reveal();
|
||||
|
||||
$provider = ($this->factory)($container, '');
|
||||
$listeners = $this->getListenersFromProvider($provider);
|
||||
|
||||
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
|
||||
$this->assertEquals([
|
||||
'foo' => [
|
||||
lazyListener($container, 'bar'),
|
||||
lazyListener($container, 'baz'),
|
||||
],
|
||||
'something' => [
|
||||
lazyListener($container, 'some_listener'),
|
||||
lazyListener($container, 'another_listener'),
|
||||
lazyListener($container, 'foobar'),
|
||||
],
|
||||
], $listeners);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function configuredAsyncEventsAreProperlyAttached(): void
|
||||
{
|
||||
$server = $this->createMock(HttpServer::class); // Some weird errors are thrown if prophesize is used
|
||||
|
||||
$containerMock = $this->prophesize(ContainerInterface::class);
|
||||
$containerMock->has('config')->willReturn(true);
|
||||
$containerMock->get('config')->willReturn([
|
||||
'events' => [
|
||||
'async' => [
|
||||
'foo' => [
|
||||
'bar',
|
||||
'baz',
|
||||
],
|
||||
'something' => [
|
||||
'some_listener',
|
||||
'another_listener',
|
||||
'foobar',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$containerMock->has(HttpServer::class)->willReturn(true);
|
||||
$containerMock->get(HttpServer::class)->willReturn($server);
|
||||
$container = $containerMock->reveal();
|
||||
|
||||
$provider = ($this->factory)($container, '');
|
||||
$listeners = $this->getListenersFromProvider($provider);
|
||||
|
||||
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
|
||||
$this->assertEquals([
|
||||
'foo' => [
|
||||
asyncListener($server, 'bar'),
|
||||
asyncListener($server, 'baz'),
|
||||
],
|
||||
'something' => [
|
||||
asyncListener($server, 'some_listener'),
|
||||
asyncListener($server, 'another_listener'),
|
||||
asyncListener($server, 'foobar'),
|
||||
],
|
||||
], $listeners);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function ignoresAsyncEventsWhenServerIsNotRegistered(): void
|
||||
{
|
||||
$containerMock = $this->prophesize(ContainerInterface::class);
|
||||
$containerMock->has('config')->willReturn(true);
|
||||
$containerMock->get('config')->willReturn([
|
||||
'events' => [
|
||||
'async' => [
|
||||
'foo' => [
|
||||
'bar',
|
||||
'baz',
|
||||
],
|
||||
'something' => [
|
||||
'some_listener',
|
||||
'another_listener',
|
||||
'foobar',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$containerMock->has(HttpServer::class)->willReturn(false);
|
||||
$container = $containerMock->reveal();
|
||||
|
||||
$provider = ($this->factory)($container, '');
|
||||
$listeners = $this->getListenersFromProvider($provider);
|
||||
|
||||
$this->assertInstanceOf(AttachableListenerProvider::class, $provider);
|
||||
$this->assertEmpty($listeners);
|
||||
}
|
||||
|
||||
private function getListenersFromProvider($provider): array
|
||||
{
|
||||
$ref = new ReflectionObject($provider);
|
||||
$prop = $ref->getProperty('listeners');
|
||||
$prop->setAccessible(true);
|
||||
|
||||
return $prop->getValue($provider);
|
||||
}
|
||||
}
|
|
@ -15,9 +15,6 @@
|
|||
<testsuite name="CLI">
|
||||
<directory>./module/CLI/test</directory>
|
||||
</testsuite>
|
||||
<testsuite name="EventDispatcher">
|
||||
<directory>./module/EventDispatcher/test</directory>
|
||||
</testsuite>
|
||||
<testsuite name="PreviewGenerator">
|
||||
<directory>./module/PreviewGenerator/test</directory>
|
||||
</testsuite>
|
||||
|
|
Loading…
Reference in a new issue